mirror of
https://github.com/jomjol/AI-on-the-edge-device.git
synced 2026-01-27 12:50:39 +03:00
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -10,6 +10,3 @@
|
||||
[submodule "code/components/stb"]
|
||||
path = code/components/stb
|
||||
url = https://github.com/nothings/stb.git
|
||||
[submodule "code/components/esp-protocols"]
|
||||
path = code/components/esp-protocols
|
||||
url = https://github.com/espressif/esp-protocols.git
|
||||
|
||||
12
code/.gitignore
vendored
12
code/.gitignore
vendored
@@ -6,11 +6,7 @@
|
||||
.vscode/ipch
|
||||
version.cpp
|
||||
sdkconfig.esp32cam
|
||||
sdkconfig.esp32cam-dev
|
||||
sdkconfig.esp32cam-debug
|
||||
sdkconfig.esp32cam-board-rev3
|
||||
sdkconfig.esp32cam-cpu-freq-240
|
||||
sdkconfig.esp32cam-board-rev3-cpu-freq-240
|
||||
sdkconfig.esp32cam-dev-himem
|
||||
sdkconfig.esp32cam-dev-task-analysis
|
||||
sdkconfig.esp32cam-no-softap
|
||||
sdkconfig.wrover-kit-esp32
|
||||
sdkconfig.freenove-esp32s3
|
||||
sdkconfig.esp32s3-eth-v1
|
||||
sdkconfig.esp32s3-eth-v2
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
cmake_minimum_required(VERSION 3.16.0)
|
||||
|
||||
list(APPEND EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common components/esp-tflite-micro components/esp-protocols/components/mdns)
|
||||
list(APPEND EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common components/esp-tflite-micro)
|
||||
|
||||
ADD_CUSTOM_COMMAND(
|
||||
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/version.cpp
|
||||
|
||||
Submodule code/components/esp-protocols deleted from 9b74256b51
@@ -1,7 +0,0 @@
|
||||
FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
|
||||
|
||||
idf_component_register(SRCS ${app_sources}
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES jomjol_logfile)
|
||||
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
#include <string.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
#include "Helper.h"
|
||||
#include "configFile.h"
|
||||
#include <esp_log.h>
|
||||
|
||||
#include "../../include/defines.h"
|
||||
|
||||
static const char *TAG = "CONFIG";
|
||||
|
||||
ConfigFile::ConfigFile(std::string filePath)
|
||||
{
|
||||
std::string config = FormatFileName(filePath);
|
||||
pFile = fopen(config.c_str(), "r");
|
||||
}
|
||||
|
||||
ConfigFile::~ConfigFile()
|
||||
{
|
||||
fclose(pFile);
|
||||
}
|
||||
|
||||
bool ConfigFile::isNewParagraph(std::string input)
|
||||
{
|
||||
if ((input[0] == '[') || ((input[0] == ';') && (input[1] == '[')))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ConfigFile::GetNextParagraph(std::string& aktparamgraph, bool &disabled, bool &eof)
|
||||
{
|
||||
while (getNextLine(&aktparamgraph, disabled, eof) && !isNewParagraph(aktparamgraph));
|
||||
|
||||
if (isNewParagraph(aktparamgraph))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ConfigFile::getNextLine(std::string *rt, bool &disabled, bool &eof)
|
||||
{
|
||||
eof = false;
|
||||
char zw[1024] = "";
|
||||
if (pFile == NULL)
|
||||
{
|
||||
*rt = "";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fgets(zw, 1024, pFile))
|
||||
{
|
||||
ESP_LOGD(TAG, "%s", zw);
|
||||
if ((strlen(zw) == 0) && feof(pFile))
|
||||
{
|
||||
*rt = "";
|
||||
eof = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*rt = "";
|
||||
eof = true;
|
||||
return false;
|
||||
}
|
||||
*rt = zw;
|
||||
*rt = trim(*rt);
|
||||
while ((zw[0] == ';' || zw[0] == '#' || (rt->size() == 0)) && !(zw[1] == '['))
|
||||
{
|
||||
fgets(zw, 1024, pFile);
|
||||
ESP_LOGD(TAG, "%s", zw);
|
||||
if (feof(pFile))
|
||||
{
|
||||
*rt = "";
|
||||
eof = true;
|
||||
return false;
|
||||
}
|
||||
*rt = zw;
|
||||
*rt = trim(*rt);
|
||||
}
|
||||
|
||||
disabled = ((*rt)[0] == ';');
|
||||
return true;
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef CONFIGFILE_H
|
||||
#define CONFIGFILE_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class ConfigFile {
|
||||
public:
|
||||
ConfigFile(std::string filePath);
|
||||
~ConfigFile();
|
||||
|
||||
bool isNewParagraph(std::string input);
|
||||
bool GetNextParagraph(std::string& aktparamgraph, bool &disabled, bool &eof);
|
||||
bool getNextLine(std::string* rt, bool &disabled, bool &eof);
|
||||
bool ConfigFileExists(){return pFile;};
|
||||
|
||||
private:
|
||||
FILE* pFile;
|
||||
};
|
||||
|
||||
#endif //CONFIGFILE_H
|
||||
@@ -1,9 +1,7 @@
|
||||
FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
|
||||
|
||||
list(APPEND EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)
|
||||
|
||||
idf_component_register(SRCS ${app_sources}
|
||||
INCLUDE_DIRS "." "../../include"
|
||||
REQUIRES esp_http_server jomjol_logfile jomjol_configfile jomjol_mqtt jomjol_flowcontroll)
|
||||
REQUIRES esp_http_server jomjol_logfile jomjol_mqtt jomjol_flowcontroll jomjol_helper)
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,57 +3,58 @@
|
||||
#ifndef SERVER_GPIO_H
|
||||
#define SERVER_GPIO_H
|
||||
|
||||
#include <esp_log.h>
|
||||
|
||||
#include <esp_http_server.h>
|
||||
#include <map>
|
||||
#include "driver/gpio.h"
|
||||
#include <driver/gpio.h>
|
||||
#include <esp_http_server.h>
|
||||
#include <esp_log.h>
|
||||
|
||||
#include "SmartLeds.h"
|
||||
|
||||
typedef enum {
|
||||
GPIO_PIN_MODE_DISABLED = 0x0,
|
||||
GPIO_PIN_MODE_INPUT = 0x1,
|
||||
GPIO_PIN_MODE_INPUT_PULLUP = 0x2,
|
||||
GPIO_PIN_MODE_INPUT_PULLDOWN = 0x3,
|
||||
GPIO_PIN_MODE_OUTPUT = 0x4,
|
||||
GPIO_PIN_MODE_BUILT_IN_FLASH_LED = 0x5,
|
||||
GPIO_PIN_MODE_OUTPUT_PWM = 0x6,
|
||||
GPIO_PIN_MODE_EXTERNAL_FLASH_PWM = 0x7,
|
||||
GPIO_PIN_MODE_EXTERNAL_FLASH_WS281X = 0x8,
|
||||
typedef enum
|
||||
{
|
||||
GPIO_PIN_MODE_DISABLED = 0x0,
|
||||
GPIO_PIN_MODE_INPUT = 0x1,
|
||||
GPIO_PIN_MODE_INPUT_PULLUP = 0x2,
|
||||
GPIO_PIN_MODE_INPUT_PULLDOWN = 0x3,
|
||||
GPIO_PIN_MODE_OUTPUT = 0x4,
|
||||
GPIO_PIN_MODE_OUTPUT_PWM = 0x5,
|
||||
GPIO_PIN_MODE_OUTPUT_WS281X = 0x6,
|
||||
GPIO_PIN_MODE_BUILTIN_FLASH = 0x7,
|
||||
GPIO_PIN_MODE_BUILTIN_FLASH_PWM = 0x8,
|
||||
} gpio_pin_mode_t;
|
||||
|
||||
struct GpioResult {
|
||||
struct GpioResult
|
||||
{
|
||||
gpio_num_t gpio;
|
||||
int value;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
GPIO_SET_SOURCE_INTERNAL = 0,
|
||||
GPIO_SET_SOURCE_MQTT = 1,
|
||||
GPIO_SET_SOURCE_HTTP = 2,
|
||||
typedef enum
|
||||
{
|
||||
GPIO_SET_SOURCE_INTERNAL = 0,
|
||||
GPIO_SET_SOURCE_MQTT = 1,
|
||||
GPIO_SET_SOURCE_HTTP = 2,
|
||||
} gpio_set_source;
|
||||
|
||||
class GpioPin {
|
||||
class GpioPin
|
||||
{
|
||||
public:
|
||||
GpioPin(gpio_num_t gpio, const char* name, gpio_pin_mode_t mode, gpio_int_type_t interruptType, uint8_t dutyResolution, std::string mqttTopic, bool httpEnable);
|
||||
GpioPin(gpio_num_t gpio, const char *name, gpio_pin_mode_t mode, gpio_int_type_t interruptType, uint8_t dutyResolution, std::string mqttTopic, bool httpEnable);
|
||||
~GpioPin();
|
||||
|
||||
void init();
|
||||
bool getValue(std::string* errorText);
|
||||
void setValue(bool value, gpio_set_source setSource, std::string* errorText);
|
||||
#ifdef ENABLE_MQTT
|
||||
bool handleMQTT(std::string, char* data, int data_len);
|
||||
#endif //ENABLE_MQTT
|
||||
bool getValue(std::string *errorText);
|
||||
void setValue(bool value, gpio_set_source setSource, std::string *errorText);
|
||||
bool handleMQTT(std::string, char *data, int data_len);
|
||||
void publishState();
|
||||
void gpioInterrupt(int value);
|
||||
gpio_int_type_t getInterruptType() { return _interruptType; }
|
||||
gpio_pin_mode_t getMode() { return _mode; }
|
||||
gpio_num_t getGPIO(){return _gpio;};
|
||||
gpio_num_t getGPIO() { return _gpio; };
|
||||
|
||||
private:
|
||||
gpio_num_t _gpio;
|
||||
const char* _name;
|
||||
const char *_name;
|
||||
gpio_pin_mode_t _mode;
|
||||
gpio_int_type_t _interruptType;
|
||||
std::string _mqttTopic;
|
||||
@@ -63,7 +64,8 @@ private:
|
||||
esp_err_t callHandleHttpRequest(httpd_req_t *req);
|
||||
void taskGpioHandler(void *pvParameter);
|
||||
|
||||
class GpioHandler {
|
||||
class GpioHandler
|
||||
{
|
||||
public:
|
||||
GpioHandler(std::string configFile, httpd_handle_t httpServer);
|
||||
~GpioHandler();
|
||||
@@ -73,22 +75,19 @@ public:
|
||||
void registerGpioUri();
|
||||
esp_err_t handleHttpRequest(httpd_req_t *req);
|
||||
void taskHandler();
|
||||
void gpioInterrupt(GpioResult* gpioResult);
|
||||
void gpioInterrupt(GpioResult *gpioResult);
|
||||
void flashLightEnable(bool value);
|
||||
bool isEnabled() { return _isEnabled; }
|
||||
#ifdef ENABLE_MQTT
|
||||
void handleMQTTconnect();
|
||||
#endif //ENABLE_MQTT
|
||||
|
||||
private:
|
||||
std::string _configFile;
|
||||
httpd_handle_t _httpServer;
|
||||
std::map<gpio_num_t, GpioPin*> *gpioMap = NULL;
|
||||
std::map<gpio_num_t, GpioPin *> *gpioMap = NULL;
|
||||
TaskHandle_t xHandleTaskGpio = NULL;
|
||||
bool _isEnabled = false;
|
||||
|
||||
int LEDNumbers = 2;
|
||||
Rgb LEDColor = Rgb{ 255, 255, 255 };
|
||||
Rgb LEDColor = Rgb{255, 255, 255};
|
||||
LedType LEDType = LED_WS2812;
|
||||
#ifdef __LEDGLOBAL
|
||||
SmartLed *leds_global = NULL;
|
||||
@@ -106,9 +105,7 @@ void gpio_handler_create(httpd_handle_t server);
|
||||
void gpio_handler_init();
|
||||
void gpio_handler_deinit();
|
||||
void gpio_handler_destroy();
|
||||
GpioHandler* gpio_handler_get();
|
||||
|
||||
GpioHandler *gpio_handler_get();
|
||||
|
||||
|
||||
#endif //SERVER_GPIO_H
|
||||
|
||||
#endif // SERVER_GPIO_H
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
|
||||
|
||||
list(APPEND EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)
|
||||
|
||||
idf_component_register(SRCS ${app_sources}
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES esp_timer esp32-camera esp_http_server jomjol_logfile jomjol_image_proc nvs_flash jomjol_fileserver_ota jomjol_controlGPIO)
|
||||
INCLUDE_DIRS "." "../../include"
|
||||
REQUIRES esp_timer esp32-camera esp_http_server jomjol_network jomjol_logfile jomjol_image_proc nvs_flash jomjol_fileserver_ota jomjol_controlGPIO jomjol_helper)
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,21 +3,21 @@
|
||||
#ifndef CLASSCONTROLLCAMERA_H
|
||||
#define CLASSCONTROLLCAMERA_H
|
||||
|
||||
#include <string>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/event_groups.h"
|
||||
|
||||
#include "esp_camera.h"
|
||||
#include <string>
|
||||
#include <esp_http_server.h>
|
||||
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/queue.h>
|
||||
#include <freertos/event_groups.h>
|
||||
|
||||
#include "defines.h"
|
||||
#include "esp_camera.h"
|
||||
#include "CImageBasis.h"
|
||||
#include "../../include/defines.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint16_t CamSensor_id;
|
||||
int CamXclkFreqMhz;
|
||||
|
||||
framesize_t ImageFrameSize = FRAMESIZE_VGA; // 0 - 10
|
||||
gainceiling_t ImageGainceiling; // Image gain (GAINCEILING_x2, x4, x8, x16, x32, x64 or x128)
|
||||
@@ -59,58 +59,74 @@ typedef struct
|
||||
int ImageZoomSize;
|
||||
|
||||
int WaitBeforePicture;
|
||||
bool isImageSize;
|
||||
|
||||
bool CameraInitSuccessful;
|
||||
bool changedCameraSettings;
|
||||
bool DemoMode;
|
||||
bool SaveAllFiles;
|
||||
} camera_controll_config_temp_t;
|
||||
|
||||
extern camera_controll_config_temp_t CCstatus;
|
||||
extern camera_controll_config_temp_t CFstatus;
|
||||
|
||||
class CCamera
|
||||
{
|
||||
protected:
|
||||
void ledc_init(void);
|
||||
bool loadNextDemoImage(camera_fb_t *fb);
|
||||
long GetFileSize(std::string filename);
|
||||
void SetCamWindow(sensor_t *s, int frameSizeX, int frameSizeY, int xOffset, int yOffset, int xTotal, int yTotal, int xOutput, int yOutput, int imageVflip);
|
||||
void SetImageWidthHeightFromResolution(framesize_t resol);
|
||||
void SanitizeZoomParams(int imageSize, int frameSizeX, int frameSizeY, int &imageWidth, int &imageHeight, int &zoomOffsetX, int &zoomOffsetY);
|
||||
bool load_next_demo_image(camera_fb_t *fb);
|
||||
long get_file_size(std::string filename);
|
||||
void set_camera_window(sensor_t *cam_sensor, int frameSizeX, int frameSizeY, int xOffset, int yOffset, int xTotal, int yTotal, int xOutput, int yOutput, int imageVflip);
|
||||
void set_image_width_height_from_resolution(framesize_t resol);
|
||||
void sanitize_zoom_params(camera_controll_config_temp_t *camConfig, int imageSize, int frameSizeX, int frameSizeY, int &imageWidth, int &imageHeight, int &zoomOffsetX, int &zoomOffsetY);
|
||||
|
||||
public:
|
||||
uint16_t CamSensorId = OV2640_PID;
|
||||
|
||||
int LedIntensity = 4096;
|
||||
bool CaptureToBasisImageLed = false;
|
||||
bool CaptureToFileLed = false;
|
||||
bool CaptureToHTTPLed = false;
|
||||
bool CaptureToStreamLed = false;
|
||||
|
||||
bool DemoMode = false;
|
||||
bool SaveAllFiles = false;
|
||||
bool ImageAntialiasing = false;
|
||||
float ImageInitialRotate = 0.0;
|
||||
bool ImageInitialFlip = false;
|
||||
|
||||
bool CamDeepSleepEnable = false;
|
||||
bool CameraInitSuccessful = false;
|
||||
bool changedCameraSettings = false;
|
||||
bool CamTempImage = false;
|
||||
|
||||
CCamera(void);
|
||||
esp_err_t InitCam(void);
|
||||
esp_err_t init_camera(void);
|
||||
void power_reset_camera(void);
|
||||
|
||||
void LightOnOff(bool status);
|
||||
void LEDOnOff(bool status);
|
||||
void set_flash_light_on_off(bool status);
|
||||
void set_blink_led_on_off(bool status);
|
||||
|
||||
esp_err_t setSensorDatenFromCCstatus(void);
|
||||
esp_err_t getSensorDatenToCCstatus(void);
|
||||
esp_err_t set_sensor_controll_config(camera_controll_config_temp_t *camConfig);
|
||||
esp_err_t get_sensor_controll_config(camera_controll_config_temp_t *camConfig);
|
||||
esp_err_t set_camera_config_from_to(camera_controll_config_temp_t *camConfigFrom, camera_controll_config_temp_t *camConfigTo);
|
||||
|
||||
int SetCamGainceiling(sensor_t *s, gainceiling_t gainceilingLevel);
|
||||
void SetCamSharpness(bool autoSharpnessEnabled, int sharpnessLevel);
|
||||
void SetCamSpecialEffect(sensor_t *s, int specialEffect);
|
||||
void SetCamContrastBrightness(sensor_t *s, int _contrast, int _brightness);
|
||||
int check_camera_settings_changed(void);
|
||||
int set_camera_deep_sleep(bool enable);
|
||||
|
||||
esp_err_t CaptureToHTTP(httpd_req_t *req, int delay = 0);
|
||||
esp_err_t CaptureToStream(httpd_req_t *req, bool FlashlightOn);
|
||||
int set_camera_gainceiling(sensor_t *cam_sensor, gainceiling_t gainceilingLevel);
|
||||
void set_camera_sharpness(bool autoSharpnessEnabled, int sharpnessLevel);
|
||||
void set_camera_special_effect(sensor_t *cam_sensor, int specialEffect);
|
||||
void set_camera_contrast_brightness(sensor_t *cam_sensor, int _contrast, int _brightness);
|
||||
|
||||
void SetQualityZoomSize(int qual, framesize_t resol, bool zoomEnabled, int zoomOffsetX, int zoomOffsetY, int imageSize, int imageVflip);
|
||||
void SetZoomSize(bool zoomEnabled, int zoomOffsetX, int zoomOffsetY, int imageSize, int imageVflip);
|
||||
esp_err_t capture_to_http(httpd_req_t *req, int flash_duration = 0);
|
||||
esp_err_t capture_to_stream(httpd_req_t *req, bool FlashlightOn);
|
||||
|
||||
int SetLEDIntensity(int _intrel);
|
||||
bool testCamera(void);
|
||||
bool getCameraInitSuccessful(void);
|
||||
void useDemoMode(void);
|
||||
void set_quality_zoom_size(camera_controll_config_temp_t *camConfig);
|
||||
void set_zoom_size(camera_controll_config_temp_t *camConfig);
|
||||
|
||||
framesize_t TextToFramesize(const char *text);
|
||||
int set_led_intensity(int _intrel);
|
||||
bool get_camera_init_successful(void);
|
||||
void use_demo_mode(void);
|
||||
|
||||
esp_err_t CaptureToFile(std::string nm, int delay = 0);
|
||||
esp_err_t CaptureToBasisImage(CImageBasis *_Image, int delay = 0);
|
||||
framesize_t text_to_framesize(const char *text);
|
||||
|
||||
esp_err_t capture_to_file(std::string file_name, int flash_duration = 0);
|
||||
esp_err_t capture_to_basis_image(CImageBasis *_Image, int flash_duration = 0);
|
||||
};
|
||||
|
||||
extern CCamera Camera;
|
||||
|
||||
@@ -1,308 +0,0 @@
|
||||
#include "server_camera.h"
|
||||
|
||||
#include <string>
|
||||
#include "string.h"
|
||||
|
||||
#include "esp_camera.h"
|
||||
#include "ClassControllCamera.h"
|
||||
#include "MainFlowControl.h"
|
||||
|
||||
#include "ClassLogFile.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#include "basic_auth.h"
|
||||
|
||||
#include "../../include/defines.h"
|
||||
|
||||
static const char *TAG = "server_cam";
|
||||
|
||||
void PowerResetCamera()
|
||||
{
|
||||
#if CAM_PIN_PWDN == GPIO_NUM_NC // Use reset only if pin is available
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "No power down pin availbale to reset camera");
|
||||
#else
|
||||
ESP_LOGD(TAG, "Resetting camera by power down line");
|
||||
gpio_config_t conf;
|
||||
conf.intr_type = GPIO_INTR_DISABLE;
|
||||
conf.pin_bit_mask = 1LL << CAM_PIN_PWDN;
|
||||
conf.mode = GPIO_MODE_OUTPUT;
|
||||
conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
|
||||
conf.pull_up_en = GPIO_PULLUP_DISABLE;
|
||||
gpio_config(&conf);
|
||||
|
||||
// carefull, logic is inverted compared to reset pin
|
||||
gpio_set_level(CAM_PIN_PWDN, 1);
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
gpio_set_level(CAM_PIN_PWDN, 0);
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
#endif
|
||||
}
|
||||
|
||||
esp_err_t handler_lightOn(httpd_req_t *req)
|
||||
{
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("handler_lightOn - Start");
|
||||
ESP_LOGD(TAG, "handler_lightOn uri: %s", req->uri);
|
||||
#endif
|
||||
|
||||
if (Camera.getCameraInitSuccessful())
|
||||
{
|
||||
Camera.LightOnOff(true);
|
||||
const char *resp_str = (const char *)req->user_ctx;
|
||||
httpd_resp_send(req, resp_str, strlen(resp_str));
|
||||
}
|
||||
else
|
||||
{
|
||||
httpd_resp_send_err(req, HTTPD_403_FORBIDDEN, "Camera not initialized: REST API /lighton not available!");
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("handler_lightOn - Done");
|
||||
#endif
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t handler_lightOff(httpd_req_t *req)
|
||||
{
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("handler_lightOff - Start");
|
||||
ESP_LOGD(TAG, "handler_lightOff uri: %s", req->uri);
|
||||
#endif
|
||||
|
||||
if (Camera.getCameraInitSuccessful())
|
||||
{
|
||||
Camera.LightOnOff(false);
|
||||
const char *resp_str = (const char *)req->user_ctx;
|
||||
httpd_resp_send(req, resp_str, strlen(resp_str));
|
||||
}
|
||||
else
|
||||
{
|
||||
httpd_resp_send_err(req, HTTPD_403_FORBIDDEN, "Camera not initialized: REST API /lightoff not available!");
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("handler_lightOff - Done");
|
||||
#endif
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t handler_capture(httpd_req_t *req)
|
||||
{
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("handler_capture - Start");
|
||||
#endif
|
||||
|
||||
if (Camera.getCameraInitSuccessful())
|
||||
{
|
||||
// If the camera settings were changed by creating a new reference image, they must be reset
|
||||
if (CFstatus.changedCameraSettings)
|
||||
{
|
||||
Camera.setSensorDatenFromCCstatus(); // CCstatus >>> Kamera
|
||||
Camera.SetQualityZoomSize(CCstatus.ImageQuality, CCstatus.ImageFrameSize, CCstatus.ImageZoomEnabled, CCstatus.ImageZoomOffsetX, CCstatus.ImageZoomOffsetY, CCstatus.ImageZoomSize, CCstatus.ImageVflip);
|
||||
Camera.LedIntensity = CCstatus.ImageLedIntensity;
|
||||
CFstatus.changedCameraSettings = false;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
ESP_LOGD(TAG, "Size: %d, Quality: %d", CCstatus.ImageFrameSize, CCstatus.ImageQuality);
|
||||
#endif
|
||||
|
||||
esp_err_t result;
|
||||
result = Camera.CaptureToHTTP(req);
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("handler_capture - Done");
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
httpd_resp_send_err(req, HTTPD_403_FORBIDDEN, "Camera not initialized: REST API /capture not available!");
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t handler_capture_with_light(httpd_req_t *req)
|
||||
{
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("handler_capture_with_light - Start");
|
||||
#endif
|
||||
|
||||
if (Camera.getCameraInitSuccessful())
|
||||
{
|
||||
char _query[100];
|
||||
char _delay[10];
|
||||
int delay = 2500;
|
||||
|
||||
if (httpd_req_get_url_query_str(req, _query, 100) == ESP_OK)
|
||||
{
|
||||
ESP_LOGD(TAG, "Query: %s", _query);
|
||||
|
||||
if (httpd_query_key_value(_query, "delay", _delay, 10) == ESP_OK)
|
||||
{
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
ESP_LOGD(TAG, "Delay: %s", _delay);
|
||||
#endif
|
||||
delay = atoi(_delay);
|
||||
|
||||
if (delay < 0)
|
||||
{
|
||||
delay = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the camera settings were changed by creating a new reference image, they must be reset
|
||||
if (CFstatus.changedCameraSettings)
|
||||
{
|
||||
Camera.setSensorDatenFromCCstatus(); // CCstatus >>> Kamera
|
||||
Camera.SetQualityZoomSize(CCstatus.ImageQuality, CCstatus.ImageFrameSize, CCstatus.ImageZoomEnabled, CCstatus.ImageZoomOffsetX, CCstatus.ImageZoomOffsetY, CCstatus.ImageZoomSize, CCstatus.ImageVflip);
|
||||
Camera.LedIntensity = CCstatus.ImageLedIntensity;
|
||||
CFstatus.changedCameraSettings = false;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
ESP_LOGD(TAG, "Size: %d, Quality: %d", CCstatus.ImageFrameSize, CCstatus.ImageQuality);
|
||||
#endif
|
||||
|
||||
Camera.LightOnOff(true);
|
||||
const TickType_t xDelay = delay / portTICK_PERIOD_MS;
|
||||
vTaskDelay(xDelay);
|
||||
|
||||
esp_err_t result;
|
||||
result = Camera.CaptureToHTTP(req);
|
||||
|
||||
Camera.LightOnOff(false);
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("handler_capture_with_light - Done");
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
httpd_resp_send_err(req, HTTPD_403_FORBIDDEN, "Camera not initialized: REST API /capture_with_flashlight not available!");
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t handler_capture_save_to_file(httpd_req_t *req)
|
||||
{
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("handler_capture_save_to_file - Start");
|
||||
#endif
|
||||
|
||||
if (Camera.getCameraInitSuccessful())
|
||||
{
|
||||
char _query[100];
|
||||
char _delay[10];
|
||||
int delay = 0;
|
||||
char filename[100];
|
||||
std::string fn = "/sdcard/";
|
||||
|
||||
if (httpd_req_get_url_query_str(req, _query, 100) == ESP_OK)
|
||||
{
|
||||
ESP_LOGD(TAG, "Query: %s", _query);
|
||||
|
||||
if (httpd_query_key_value(_query, "filename", filename, 100) == ESP_OK)
|
||||
{
|
||||
fn.append(filename);
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
ESP_LOGD(TAG, "Filename: %s", fn.c_str());
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
fn.append("noname.jpg");
|
||||
}
|
||||
|
||||
if (httpd_query_key_value(_query, "delay", _delay, 10) == ESP_OK)
|
||||
{
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
ESP_LOGD(TAG, "Delay: %s", _delay);
|
||||
#endif
|
||||
delay = atoi(_delay);
|
||||
|
||||
if (delay < 0)
|
||||
{
|
||||
delay = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fn.append("noname.jpg");
|
||||
}
|
||||
|
||||
// If the camera settings were changed by creating a new reference image, they must be reset
|
||||
if (CFstatus.changedCameraSettings)
|
||||
{
|
||||
Camera.setSensorDatenFromCCstatus(); // CCstatus >>> Kamera
|
||||
Camera.SetQualityZoomSize(CCstatus.ImageQuality, CCstatus.ImageFrameSize, CCstatus.ImageZoomEnabled, CCstatus.ImageZoomOffsetX, CCstatus.ImageZoomOffsetY, CCstatus.ImageZoomSize, CCstatus.ImageVflip);
|
||||
Camera.LedIntensity = CCstatus.ImageLedIntensity;
|
||||
CFstatus.changedCameraSettings = false;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
ESP_LOGD(TAG, "Size: %d, Quality: %d", CCstatus.ImageFrameSize, CCstatus.ImageQuality);
|
||||
#endif
|
||||
|
||||
esp_err_t result;
|
||||
result = Camera.CaptureToFile(fn, delay);
|
||||
|
||||
const char *resp_str = (const char *)fn.c_str();
|
||||
httpd_resp_send(req, resp_str, strlen(resp_str));
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("handler_capture_save_to_file - Done");
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
httpd_resp_send_err(req, HTTPD_403_FORBIDDEN, "Camera not initialized: REST API /save not available!");
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
|
||||
void register_server_camera_uri(httpd_handle_t server)
|
||||
{
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
ESP_LOGI(TAG, "server_part_camera - Registering URI handlers");
|
||||
#endif
|
||||
|
||||
httpd_uri_t camuri = {};
|
||||
camuri.method = HTTP_GET;
|
||||
|
||||
camuri.uri = "/lighton";
|
||||
camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_lightOn);
|
||||
camuri.user_ctx = (void *)"Light On";
|
||||
httpd_register_uri_handler(server, &camuri);
|
||||
|
||||
camuri.uri = "/lightoff";
|
||||
camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_lightOff);
|
||||
camuri.user_ctx = (void *)"Light Off";
|
||||
httpd_register_uri_handler(server, &camuri);
|
||||
|
||||
camuri.uri = "/capture";
|
||||
camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_capture);
|
||||
camuri.user_ctx = NULL;
|
||||
httpd_register_uri_handler(server, &camuri);
|
||||
|
||||
camuri.uri = "/capture_with_flashlight";
|
||||
camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_capture_with_light);
|
||||
camuri.user_ctx = NULL;
|
||||
httpd_register_uri_handler(server, &camuri);
|
||||
|
||||
camuri.uri = "/save";
|
||||
camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_capture_save_to_file);
|
||||
camuri.user_ctx = NULL;
|
||||
httpd_register_uri_handler(server, &camuri);
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef JOMJOL_CONTROLCAMERA_H
|
||||
#define JOMJOL_CONTROLCAMERA_H
|
||||
|
||||
#include <esp_log.h>
|
||||
|
||||
#include <esp_http_server.h>
|
||||
|
||||
//#include "ClassControllCamera.h"
|
||||
|
||||
void register_server_camera_uri(httpd_handle_t server);
|
||||
void PowerResetCamera();
|
||||
|
||||
#endif
|
||||
@@ -1,7 +1,7 @@
|
||||
FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
|
||||
|
||||
idf_component_register(SRCS ${app_sources}
|
||||
INCLUDE_DIRS "." "../../include" "miniz"
|
||||
REQUIRES vfs esp_http_server app_update esp_http_client nvs_flash jomjol_tfliteclass jomjol_flowcontroll spiffs jomjol_helper jomjol_controlGPIO)
|
||||
INCLUDE_DIRS "." "../../include"
|
||||
REQUIRES vfs esp_rom esp_http_server app_update esp_http_client nvs_flash jomjol_tfliteclass jomjol_flowcontroll spiffs jomjol_helper jomjol_controlGPIO)
|
||||
|
||||
|
||||
|
||||
@@ -187,8 +187,6 @@ const char *mz_version(void)
|
||||
|
||||
#ifndef MINIZ_NO_ZLIB_APIS
|
||||
|
||||
#ifndef MINIZ_NO_DEFLATE_APIS
|
||||
|
||||
int mz_deflateInit(mz_streamp pStream, int level)
|
||||
{
|
||||
return mz_deflateInit2(pStream, level, MZ_DEFLATED, MZ_DEFAULT_WINDOW_BITS, 9, MZ_DEFAULT_STRATEGY);
|
||||
@@ -323,7 +321,7 @@ int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char
|
||||
memset(&stream, 0, sizeof(stream));
|
||||
|
||||
/* In case mz_ulong is 64-bits (argh I hate longs). */
|
||||
if ((mz_uint64)(source_len | *pDest_len) > 0xFFFFFFFFU)
|
||||
if ((source_len | *pDest_len) > 0xFFFFFFFFU)
|
||||
return MZ_PARAM_ERROR;
|
||||
|
||||
stream.next_in = pSource;
|
||||
@@ -356,10 +354,6 @@ mz_ulong mz_compressBound(mz_ulong source_len)
|
||||
return mz_deflateBound(NULL, source_len);
|
||||
}
|
||||
|
||||
#endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/
|
||||
|
||||
#ifndef MINIZ_NO_INFLATE_APIS
|
||||
|
||||
typedef struct
|
||||
{
|
||||
tinfl_decompressor m_decomp;
|
||||
@@ -566,7 +560,7 @@ int mz_uncompress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned cha
|
||||
memset(&stream, 0, sizeof(stream));
|
||||
|
||||
/* In case mz_ulong is 64-bits (argh I hate longs). */
|
||||
if ((mz_uint64)(*pSource_len | *pDest_len) > 0xFFFFFFFFU)
|
||||
if ((*pSource_len | *pDest_len) > 0xFFFFFFFFU)
|
||||
return MZ_PARAM_ERROR;
|
||||
|
||||
stream.next_in = pSource;
|
||||
@@ -595,8 +589,6 @@ int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char
|
||||
return mz_uncompress2(pDest, pDest_len, pSource, &source_len);
|
||||
}
|
||||
|
||||
#endif /*#ifndef MINIZ_NO_INFLATE_APIS*/
|
||||
|
||||
const char *mz_error(int err)
|
||||
{
|
||||
static struct
|
||||
@@ -674,8 +666,6 @@ const char *mz_error(int err)
|
||||
|
||||
|
||||
|
||||
#ifndef MINIZ_NO_DEFLATE_APIS
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@@ -754,7 +744,7 @@ static tdefl_sym_freq *tdefl_radix_sort_syms(mz_uint num_syms, tdefl_sym_freq *p
|
||||
{
|
||||
mz_uint32 total_passes = 2, pass_shift, pass, i, hist[256 * 2];
|
||||
tdefl_sym_freq *pCur_syms = pSyms0, *pNew_syms = pSyms1;
|
||||
MZ_CLEAR_ARR(hist);
|
||||
MZ_CLEAR_OBJ(hist);
|
||||
for (i = 0; i < num_syms; i++)
|
||||
{
|
||||
mz_uint freq = pSyms0[i].m_key;
|
||||
@@ -872,7 +862,7 @@ static void tdefl_optimize_huffman_table(tdefl_compressor *d, int table_num, int
|
||||
{
|
||||
int i, j, l, num_codes[1 + TDEFL_MAX_SUPPORTED_HUFF_CODESIZE];
|
||||
mz_uint next_code[TDEFL_MAX_SUPPORTED_HUFF_CODESIZE + 1];
|
||||
MZ_CLEAR_ARR(num_codes);
|
||||
MZ_CLEAR_OBJ(num_codes);
|
||||
if (static_table)
|
||||
{
|
||||
for (i = 0; i < table_len; i++)
|
||||
@@ -898,8 +888,8 @@ static void tdefl_optimize_huffman_table(tdefl_compressor *d, int table_num, int
|
||||
|
||||
tdefl_huffman_enforce_max_code_size(num_codes, num_used_syms, code_size_limit);
|
||||
|
||||
MZ_CLEAR_ARR(d->m_huff_code_sizes[table_num]);
|
||||
MZ_CLEAR_ARR(d->m_huff_codes[table_num]);
|
||||
MZ_CLEAR_OBJ(d->m_huff_code_sizes[table_num]);
|
||||
MZ_CLEAR_OBJ(d->m_huff_codes[table_num]);
|
||||
for (i = 1, j = num_used_syms; i <= code_size_limit; i++)
|
||||
for (l = num_codes[i]; l > 0; l--)
|
||||
d->m_huff_code_sizes[table_num][pSyms[--j].m_sym_index] = (mz_uint8)(i);
|
||||
@@ -985,7 +975,7 @@ static void tdefl_optimize_huffman_table(tdefl_compressor *d, int table_num, int
|
||||
} \
|
||||
}
|
||||
|
||||
static const mz_uint8 s_tdefl_packed_code_size_syms_swizzle[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };
|
||||
static mz_uint8 s_tdefl_packed_code_size_syms_swizzle[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };
|
||||
|
||||
static void tdefl_start_dynamic_block(tdefl_compressor *d)
|
||||
{
|
||||
@@ -1123,8 +1113,7 @@ static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d)
|
||||
if (flags & 1)
|
||||
{
|
||||
mz_uint s0, s1, n0, n1, sym, num_extra_bits;
|
||||
mz_uint match_len = pLZ_codes[0];
|
||||
mz_uint match_dist = (pLZ_codes[1] | (pLZ_codes[2] << 8));
|
||||
mz_uint match_len = pLZ_codes[0], match_dist = *(const mz_uint16 *)(pLZ_codes + 1);
|
||||
pLZ_codes += 3;
|
||||
|
||||
MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]);
|
||||
@@ -1169,7 +1158,7 @@ static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d)
|
||||
if (pOutput_buf >= d->m_pOutput_buf_end)
|
||||
return MZ_FALSE;
|
||||
|
||||
memcpy(pOutput_buf, &bit_buffer, sizeof(mz_uint64));
|
||||
*(mz_uint64 *)pOutput_buf = bit_buffer;
|
||||
pOutput_buf += (bits_in >> 3);
|
||||
bit_buffer >>= (bits_in & ~7);
|
||||
bits_in &= 7;
|
||||
@@ -1251,8 +1240,6 @@ static mz_bool tdefl_compress_block(tdefl_compressor *d, mz_bool static_block)
|
||||
return tdefl_compress_lz_codes(d);
|
||||
}
|
||||
|
||||
static const mz_uint s_tdefl_num_probes[11];
|
||||
|
||||
static int tdefl_flush_block(tdefl_compressor *d, int flush)
|
||||
{
|
||||
mz_uint saved_bit_buf, saved_bits_in;
|
||||
@@ -1273,27 +1260,8 @@ static int tdefl_flush_block(tdefl_compressor *d, int flush)
|
||||
|
||||
if ((d->m_flags & TDEFL_WRITE_ZLIB_HEADER) && (!d->m_block_index))
|
||||
{
|
||||
const mz_uint8 cmf = 0x78;
|
||||
mz_uint8 flg, flevel = 3;
|
||||
mz_uint header, i, mz_un = sizeof(s_tdefl_num_probes) / sizeof(mz_uint);
|
||||
|
||||
/* Determine compression level by reversing the process in tdefl_create_comp_flags_from_zip_params() */
|
||||
for (i = 0; i < mz_un; i++)
|
||||
if (s_tdefl_num_probes[i] == (d->m_flags & 0xFFF)) break;
|
||||
|
||||
if (i < 2)
|
||||
flevel = 0;
|
||||
else if (i < 6)
|
||||
flevel = 1;
|
||||
else if (i == 6)
|
||||
flevel = 2;
|
||||
|
||||
header = cmf << 8 | (flevel << 6);
|
||||
header += 31 - (header % 31);
|
||||
flg = header & 0xFF;
|
||||
|
||||
TDEFL_PUT_BITS(cmf, 8);
|
||||
TDEFL_PUT_BITS(flg, 8);
|
||||
TDEFL_PUT_BITS(0x78, 8);
|
||||
TDEFL_PUT_BITS(0x01, 8);
|
||||
}
|
||||
|
||||
TDEFL_PUT_BITS(flush == TDEFL_FINISH, 1);
|
||||
@@ -1764,7 +1732,7 @@ static mz_bool tdefl_compress_normal(tdefl_compressor *d)
|
||||
mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK, ins_pos = d->m_lookahead_pos + d->m_lookahead_size - 2;
|
||||
mz_uint hash = (d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK];
|
||||
mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(src_buf_left, TDEFL_MAX_MATCH_LEN - d->m_lookahead_size);
|
||||
const mz_uint8 *pSrc_end = pSrc ? pSrc + num_bytes_to_process : NULL;
|
||||
const mz_uint8 *pSrc_end = pSrc + num_bytes_to_process;
|
||||
src_buf_left -= num_bytes_to_process;
|
||||
d->m_lookahead_size += num_bytes_to_process;
|
||||
while (pSrc != pSrc_end)
|
||||
@@ -1974,8 +1942,8 @@ tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pI
|
||||
d->m_finished = (flush == TDEFL_FINISH);
|
||||
if (flush == TDEFL_FULL_FLUSH)
|
||||
{
|
||||
MZ_CLEAR_ARR(d->m_hash);
|
||||
MZ_CLEAR_ARR(d->m_next);
|
||||
MZ_CLEAR_OBJ(d->m_hash);
|
||||
MZ_CLEAR_OBJ(d->m_next);
|
||||
d->m_dict_size = 0;
|
||||
}
|
||||
}
|
||||
@@ -1998,7 +1966,7 @@ tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_fun
|
||||
d->m_greedy_parsing = (flags & TDEFL_GREEDY_PARSING_FLAG) != 0;
|
||||
d->m_max_probes[1] = 1 + (((flags & 0xFFF) >> 2) + 2) / 3;
|
||||
if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG))
|
||||
MZ_CLEAR_ARR(d->m_hash);
|
||||
MZ_CLEAR_OBJ(d->m_hash);
|
||||
d->m_lookahead_pos = d->m_lookahead_size = d->m_dict_size = d->m_total_lz_bytes = d->m_lz_code_buf_dict_pos = d->m_bits_in = 0;
|
||||
d->m_output_flush_ofs = d->m_output_flush_remaining = d->m_finished = d->m_block_index = d->m_bit_buffer = d->m_wants_to_finish = 0;
|
||||
d->m_pLZ_code_buf = d->m_lz_code_buf + 1;
|
||||
@@ -2019,7 +1987,7 @@ tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_fun
|
||||
d->m_src_buf_left = 0;
|
||||
d->m_out_buf_ofs = 0;
|
||||
if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG))
|
||||
MZ_CLEAR_ARR(d->m_dict);
|
||||
MZ_CLEAR_OBJ(d->m_dict);
|
||||
memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0);
|
||||
memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1);
|
||||
return TDEFL_STATUS_OKAY;
|
||||
@@ -2229,7 +2197,7 @@ void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h,
|
||||
/* Allocate the tdefl_compressor and tinfl_decompressor structures in C so that */
|
||||
/* non-C language bindings to tdefL_ and tinfl_ API don't need to worry about */
|
||||
/* structure size and allocation mechanism. */
|
||||
tdefl_compressor *tdefl_compressor_alloc(void)
|
||||
tdefl_compressor *tdefl_compressor_alloc()
|
||||
{
|
||||
return (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor));
|
||||
}
|
||||
@@ -2247,8 +2215,6 @@ void tdefl_compressor_free(tdefl_compressor *pComp)
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright 2013-2014 RAD Game Tools and Valve Software
|
||||
@@ -2277,8 +2243,6 @@ void tdefl_compressor_free(tdefl_compressor *pComp)
|
||||
|
||||
|
||||
|
||||
#ifndef MINIZ_NO_INFLATE_APIS
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@@ -2359,10 +2323,10 @@ extern "C" {
|
||||
/* It reads just enough bytes from the input stream that are needed to decode the next Huffman code (and absolutely no more). It works by trying to fully decode a */
|
||||
/* Huffman code by using whatever bits are currently present in the bit buffer. If this fails, it reads another byte, and tries again until it succeeds or until the */
|
||||
/* bit buffer contains >=15 bits (deflate's max. Huffman code size). */
|
||||
#define TINFL_HUFF_BITBUF_FILL(state_index, pLookUp, pTree) \
|
||||
#define TINFL_HUFF_BITBUF_FILL(state_index, pHuff) \
|
||||
do \
|
||||
{ \
|
||||
temp = pLookUp[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]; \
|
||||
temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]; \
|
||||
if (temp >= 0) \
|
||||
{ \
|
||||
code_len = temp >> 9; \
|
||||
@@ -2374,7 +2338,7 @@ extern "C" {
|
||||
code_len = TINFL_FAST_LOOKUP_BITS; \
|
||||
do \
|
||||
{ \
|
||||
temp = pTree[~temp + ((bit_buf >> code_len++) & 1)]; \
|
||||
temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \
|
||||
} while ((temp < 0) && (num_bits >= (code_len + 1))); \
|
||||
if (temp >= 0) \
|
||||
break; \
|
||||
@@ -2390,7 +2354,7 @@ extern "C" {
|
||||
/* The slow path is only executed at the very end of the input buffer. */
|
||||
/* v1.16: The original macro handled the case at the very end of the passed-in input buffer, but we also need to handle the case where the user passes in 1+zillion bytes */
|
||||
/* following the deflate data and our non-conservative read-ahead path won't kick in here on this code. This is much trickier. */
|
||||
#define TINFL_HUFF_DECODE(state_index, sym, pLookUp, pTree) \
|
||||
#define TINFL_HUFF_DECODE(state_index, sym, pHuff) \
|
||||
do \
|
||||
{ \
|
||||
int temp; \
|
||||
@@ -2399,7 +2363,7 @@ extern "C" {
|
||||
{ \
|
||||
if ((pIn_buf_end - pIn_buf_cur) < 2) \
|
||||
{ \
|
||||
TINFL_HUFF_BITBUF_FILL(state_index, pLookUp, pTree); \
|
||||
TINFL_HUFF_BITBUF_FILL(state_index, pHuff); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
@@ -2408,14 +2372,14 @@ extern "C" {
|
||||
num_bits += 16; \
|
||||
} \
|
||||
} \
|
||||
if ((temp = pLookUp[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) \
|
||||
if ((temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) \
|
||||
code_len = temp >> 9, temp &= 511; \
|
||||
else \
|
||||
{ \
|
||||
code_len = TINFL_FAST_LOOKUP_BITS; \
|
||||
do \
|
||||
{ \
|
||||
temp = pTree[~temp + ((bit_buf >> code_len++) & 1)]; \
|
||||
temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \
|
||||
} while (temp < 0); \
|
||||
} \
|
||||
sym = temp; \
|
||||
@@ -2424,33 +2388,20 @@ extern "C" {
|
||||
} \
|
||||
MZ_MACRO_END
|
||||
|
||||
static void tinfl_clear_tree(tinfl_decompressor *r)
|
||||
{
|
||||
if (r->m_type == 0)
|
||||
MZ_CLEAR_ARR(r->m_tree_0);
|
||||
else if (r->m_type == 1)
|
||||
MZ_CLEAR_ARR(r->m_tree_1);
|
||||
else
|
||||
MZ_CLEAR_ARR(r->m_tree_2);
|
||||
}
|
||||
|
||||
tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags)
|
||||
{
|
||||
static const mz_uint16 s_length_base[31] = { 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 };
|
||||
static const mz_uint8 s_length_extra[31] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 0, 0 };
|
||||
static const mz_uint16 s_dist_base[32] = { 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 0, 0 };
|
||||
static const mz_uint8 s_dist_extra[32] = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 };
|
||||
static const int s_length_base[31] = { 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 };
|
||||
static const int s_length_extra[31] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 0, 0 };
|
||||
static const int s_dist_base[32] = { 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 0, 0 };
|
||||
static const int s_dist_extra[32] = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 };
|
||||
static const mz_uint8 s_length_dezigzag[19] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };
|
||||
static const mz_uint16 s_min_table_sizes[3] = { 257, 1, 4 };
|
||||
|
||||
mz_int16 *pTrees[3];
|
||||
mz_uint8 *pCode_sizes[3];
|
||||
static const int s_min_table_sizes[3] = { 257, 1, 4 };
|
||||
|
||||
tinfl_status status = TINFL_STATUS_FAILED;
|
||||
mz_uint32 num_bits, dist, counter, num_extra;
|
||||
tinfl_bit_buf_t bit_buf;
|
||||
const mz_uint8 *pIn_buf_cur = pIn_buf_next, *const pIn_buf_end = pIn_buf_next + *pIn_buf_size;
|
||||
mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = pOut_buf_next ? pOut_buf_next + *pOut_buf_size : NULL;
|
||||
mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = pOut_buf_next + *pOut_buf_size;
|
||||
size_t out_buf_size_mask = (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF) ? (size_t)-1 : ((pOut_buf_next - pOut_buf_start) + *pOut_buf_size) - 1, dist_from_out_buf_start;
|
||||
|
||||
/* Ensure the output buffer's size is a power of 2, unless the output buffer is large enough to hold the entire output file (in which case it doesn't matter). */
|
||||
@@ -2460,13 +2411,6 @@ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_nex
|
||||
return TINFL_STATUS_BAD_PARAM;
|
||||
}
|
||||
|
||||
pTrees[0] = r->m_tree_0;
|
||||
pTrees[1] = r->m_tree_1;
|
||||
pTrees[2] = r->m_tree_2;
|
||||
pCode_sizes[0] = r->m_code_size_0;
|
||||
pCode_sizes[1] = r->m_code_size_1;
|
||||
pCode_sizes[2] = r->m_code_size_2;
|
||||
|
||||
num_bits = r->m_num_bits;
|
||||
bit_buf = r->m_bit_buf;
|
||||
dist = r->m_dist;
|
||||
@@ -2483,7 +2427,7 @@ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_nex
|
||||
TINFL_GET_BYTE(2, r->m_zhdr1);
|
||||
counter = (((r->m_zhdr0 * 256 + r->m_zhdr1) % 31 != 0) || (r->m_zhdr1 & 32) || ((r->m_zhdr0 & 15) != 8));
|
||||
if (!(decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))
|
||||
counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || ((out_buf_size_mask + 1) < (size_t)((size_t)1 << (8U + (r->m_zhdr0 >> 4)))));
|
||||
counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || ((out_buf_size_mask + 1) < (size_t)(1U << (8U + (r->m_zhdr0 >> 4)))));
|
||||
if (counter)
|
||||
{
|
||||
TINFL_CR_RETURN_FOREVER(36, TINFL_STATUS_FAILED);
|
||||
@@ -2544,11 +2488,11 @@ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_nex
|
||||
{
|
||||
if (r->m_type == 1)
|
||||
{
|
||||
mz_uint8 *p = r->m_code_size_0;
|
||||
mz_uint8 *p = r->m_tables[0].m_code_size;
|
||||
mz_uint i;
|
||||
r->m_table_sizes[0] = 288;
|
||||
r->m_table_sizes[1] = 32;
|
||||
TINFL_MEMSET(r->m_code_size_1, 5, 32);
|
||||
TINFL_MEMSET(r->m_tables[1].m_code_size, 5, 32);
|
||||
for (i = 0; i <= 143; ++i)
|
||||
*p++ = 8;
|
||||
for (; i <= 255; ++i)
|
||||
@@ -2565,30 +2509,26 @@ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_nex
|
||||
TINFL_GET_BITS(11, r->m_table_sizes[counter], "\05\05\04"[counter]);
|
||||
r->m_table_sizes[counter] += s_min_table_sizes[counter];
|
||||
}
|
||||
MZ_CLEAR_ARR(r->m_code_size_2);
|
||||
MZ_CLEAR_OBJ(r->m_tables[2].m_code_size);
|
||||
for (counter = 0; counter < r->m_table_sizes[2]; counter++)
|
||||
{
|
||||
mz_uint s;
|
||||
TINFL_GET_BITS(14, s, 3);
|
||||
r->m_code_size_2[s_length_dezigzag[counter]] = (mz_uint8)s;
|
||||
r->m_tables[2].m_code_size[s_length_dezigzag[counter]] = (mz_uint8)s;
|
||||
}
|
||||
r->m_table_sizes[2] = 19;
|
||||
}
|
||||
for (; (int)r->m_type >= 0; r->m_type--)
|
||||
{
|
||||
int tree_next, tree_cur;
|
||||
mz_int16 *pLookUp;
|
||||
mz_int16 *pTree;
|
||||
mz_uint8 *pCode_size;
|
||||
tinfl_huff_table *pTable;
|
||||
mz_uint i, j, used_syms, total, sym_index, next_code[17], total_syms[16];
|
||||
pLookUp = r->m_look_up[r->m_type];
|
||||
pTree = pTrees[r->m_type];
|
||||
pCode_size = pCode_sizes[r->m_type];
|
||||
MZ_CLEAR_ARR(total_syms);
|
||||
TINFL_MEMSET(pLookUp, 0, sizeof(r->m_look_up[0]));
|
||||
tinfl_clear_tree(r);
|
||||
pTable = &r->m_tables[r->m_type];
|
||||
MZ_CLEAR_OBJ(total_syms);
|
||||
MZ_CLEAR_OBJ(pTable->m_look_up);
|
||||
MZ_CLEAR_OBJ(pTable->m_tree);
|
||||
for (i = 0; i < r->m_table_sizes[r->m_type]; ++i)
|
||||
total_syms[pCode_size[i]]++;
|
||||
total_syms[pTable->m_code_size[i]]++;
|
||||
used_syms = 0, total = 0;
|
||||
next_code[0] = next_code[1] = 0;
|
||||
for (i = 1; i <= 15; ++i)
|
||||
@@ -2602,7 +2542,7 @@ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_nex
|
||||
}
|
||||
for (tree_next = -1, sym_index = 0; sym_index < r->m_table_sizes[r->m_type]; ++sym_index)
|
||||
{
|
||||
mz_uint rev_code = 0, l, cur_code, code_size = pCode_size[sym_index];
|
||||
mz_uint rev_code = 0, l, cur_code, code_size = pTable->m_code_size[sym_index];
|
||||
if (!code_size)
|
||||
continue;
|
||||
cur_code = next_code[code_size]++;
|
||||
@@ -2613,14 +2553,14 @@ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_nex
|
||||
mz_int16 k = (mz_int16)((code_size << 9) | sym_index);
|
||||
while (rev_code < TINFL_FAST_LOOKUP_SIZE)
|
||||
{
|
||||
pLookUp[rev_code] = k;
|
||||
pTable->m_look_up[rev_code] = k;
|
||||
rev_code += (1 << code_size);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (0 == (tree_cur = pLookUp[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)]))
|
||||
if (0 == (tree_cur = pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)]))
|
||||
{
|
||||
pLookUp[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)] = (mz_int16)tree_next;
|
||||
pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)] = (mz_int16)tree_next;
|
||||
tree_cur = tree_next;
|
||||
tree_next -= 2;
|
||||
}
|
||||
@@ -2628,24 +2568,24 @@ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_nex
|
||||
for (j = code_size; j > (TINFL_FAST_LOOKUP_BITS + 1); j--)
|
||||
{
|
||||
tree_cur -= ((rev_code >>= 1) & 1);
|
||||
if (!pTree[-tree_cur - 1])
|
||||
if (!pTable->m_tree[-tree_cur - 1])
|
||||
{
|
||||
pTree[-tree_cur - 1] = (mz_int16)tree_next;
|
||||
pTable->m_tree[-tree_cur - 1] = (mz_int16)tree_next;
|
||||
tree_cur = tree_next;
|
||||
tree_next -= 2;
|
||||
}
|
||||
else
|
||||
tree_cur = pTree[-tree_cur - 1];
|
||||
tree_cur = pTable->m_tree[-tree_cur - 1];
|
||||
}
|
||||
tree_cur -= ((rev_code >>= 1) & 1);
|
||||
pTree[-tree_cur - 1] = (mz_int16)sym_index;
|
||||
pTable->m_tree[-tree_cur - 1] = (mz_int16)sym_index;
|
||||
}
|
||||
if (r->m_type == 2)
|
||||
{
|
||||
for (counter = 0; counter < (r->m_table_sizes[0] + r->m_table_sizes[1]);)
|
||||
{
|
||||
mz_uint s;
|
||||
TINFL_HUFF_DECODE(16, dist, r->m_look_up[2], r->m_tree_2);
|
||||
TINFL_HUFF_DECODE(16, dist, &r->m_tables[2]);
|
||||
if (dist < 16)
|
||||
{
|
||||
r->m_len_codes[counter++] = (mz_uint8)dist;
|
||||
@@ -2665,8 +2605,8 @@ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_nex
|
||||
{
|
||||
TINFL_CR_RETURN_FOREVER(21, TINFL_STATUS_FAILED);
|
||||
}
|
||||
TINFL_MEMCPY(r->m_code_size_0, r->m_len_codes, r->m_table_sizes[0]);
|
||||
TINFL_MEMCPY(r->m_code_size_1, r->m_len_codes + r->m_table_sizes[0], r->m_table_sizes[1]);
|
||||
TINFL_MEMCPY(r->m_tables[0].m_code_size, r->m_len_codes, r->m_table_sizes[0]);
|
||||
TINFL_MEMCPY(r->m_tables[1].m_code_size, r->m_len_codes + r->m_table_sizes[0], r->m_table_sizes[1]);
|
||||
}
|
||||
}
|
||||
for (;;)
|
||||
@@ -2676,7 +2616,7 @@ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_nex
|
||||
{
|
||||
if (((pIn_buf_end - pIn_buf_cur) < 4) || ((pOut_buf_end - pOut_buf_cur) < 2))
|
||||
{
|
||||
TINFL_HUFF_DECODE(23, counter, r->m_look_up[0], r->m_tree_0);
|
||||
TINFL_HUFF_DECODE(23, counter, &r->m_tables[0]);
|
||||
if (counter >= 256)
|
||||
break;
|
||||
while (pOut_buf_cur >= pOut_buf_end)
|
||||
@@ -2704,14 +2644,14 @@ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_nex
|
||||
num_bits += 16;
|
||||
}
|
||||
#endif
|
||||
if ((sym2 = r->m_look_up[0][bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0)
|
||||
if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0)
|
||||
code_len = sym2 >> 9;
|
||||
else
|
||||
{
|
||||
code_len = TINFL_FAST_LOOKUP_BITS;
|
||||
do
|
||||
{
|
||||
sym2 = r->m_tree_0[~sym2 + ((bit_buf >> code_len++) & 1)];
|
||||
sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)];
|
||||
} while (sym2 < 0);
|
||||
}
|
||||
counter = sym2;
|
||||
@@ -2728,14 +2668,14 @@ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_nex
|
||||
num_bits += 16;
|
||||
}
|
||||
#endif
|
||||
if ((sym2 = r->m_look_up[0][bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0)
|
||||
if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0)
|
||||
code_len = sym2 >> 9;
|
||||
else
|
||||
{
|
||||
code_len = TINFL_FAST_LOOKUP_BITS;
|
||||
do
|
||||
{
|
||||
sym2 = r->m_tree_0[~sym2 + ((bit_buf >> code_len++) & 1)];
|
||||
sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)];
|
||||
} while (sym2 < 0);
|
||||
}
|
||||
bit_buf >>= code_len;
|
||||
@@ -2764,7 +2704,7 @@ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_nex
|
||||
counter += extra_bits;
|
||||
}
|
||||
|
||||
TINFL_HUFF_DECODE(26, dist, r->m_look_up[1], r->m_tree_1);
|
||||
TINFL_HUFF_DECODE(26, dist, &r->m_tables[1]);
|
||||
num_extra = s_dist_extra[dist];
|
||||
dist = s_dist_base[dist];
|
||||
if (num_extra)
|
||||
@@ -2849,7 +2789,7 @@ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_nex
|
||||
--pIn_buf_cur;
|
||||
num_bits -= 8;
|
||||
}
|
||||
bit_buf &= ~(~(tinfl_bit_buf_t)0 << num_bits);
|
||||
bit_buf &= (tinfl_bit_buf_t)((((mz_uint64)1) << num_bits) - (mz_uint64)1);
|
||||
MZ_ASSERT(!num_bits); /* if this assert fires then we've read beyond the end of non-deflate/zlib streams with following data (such as gzip streams). */
|
||||
|
||||
if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER)
|
||||
@@ -2881,7 +2821,7 @@ common_exit:
|
||||
}
|
||||
}
|
||||
r->m_num_bits = num_bits;
|
||||
r->m_bit_buf = bit_buf & ~(~(tinfl_bit_buf_t)0 << num_bits);
|
||||
r->m_bit_buf = bit_buf & (tinfl_bit_buf_t)((((mz_uint64)1) << num_bits) - (mz_uint64)1);
|
||||
r->m_dist = dist;
|
||||
r->m_counter = counter;
|
||||
r->m_num_extra = num_extra;
|
||||
@@ -2976,7 +2916,6 @@ int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size,
|
||||
size_t in_buf_ofs = 0, dict_ofs = 0;
|
||||
if (!pDict)
|
||||
return TINFL_STATUS_FAILED;
|
||||
memset(pDict,0,TINFL_LZ_DICT_SIZE);
|
||||
tinfl_init(&decomp);
|
||||
for (;;)
|
||||
{
|
||||
@@ -2999,7 +2938,7 @@ int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size,
|
||||
}
|
||||
|
||||
#ifndef MINIZ_NO_MALLOC
|
||||
tinfl_decompressor *tinfl_decompressor_alloc(void)
|
||||
tinfl_decompressor *tinfl_decompressor_alloc()
|
||||
{
|
||||
tinfl_decompressor *pDecomp = (tinfl_decompressor *)MZ_MALLOC(sizeof(tinfl_decompressor));
|
||||
if (pDecomp)
|
||||
@@ -3016,8 +2955,6 @@ void tinfl_decompressor_free(tinfl_decompressor *pDecomp)
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /*#ifndef MINIZ_NO_INFLATE_APIS*/
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright 2013-2014 RAD Game Tools and Valve Software
|
||||
@@ -3060,48 +2997,19 @@ extern "C" {
|
||||
#include <sys/stat.h>
|
||||
|
||||
#if defined(_MSC_VER) || defined(__MINGW64__)
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
static WCHAR* mz_utf8z_to_widechar(const char* str)
|
||||
{
|
||||
int reqChars = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);
|
||||
WCHAR* wStr = (WCHAR*)malloc(reqChars * sizeof(WCHAR));
|
||||
MultiByteToWideChar(CP_UTF8, 0, str, -1, wStr, sizeof(WCHAR) * reqChars);
|
||||
return wStr;
|
||||
}
|
||||
|
||||
static FILE *mz_fopen(const char *pFilename, const char *pMode)
|
||||
{
|
||||
WCHAR* wFilename = mz_utf8z_to_widechar(pFilename);
|
||||
WCHAR* wMode = mz_utf8z_to_widechar(pMode);
|
||||
FILE* pFile = NULL;
|
||||
errno_t err = _wfopen_s(&pFile, wFilename, wMode);
|
||||
free(wFilename);
|
||||
free(wMode);
|
||||
return err ? NULL : pFile;
|
||||
FILE *pFile = NULL;
|
||||
fopen_s(&pFile, pFilename, pMode);
|
||||
return pFile;
|
||||
}
|
||||
|
||||
static FILE *mz_freopen(const char *pPath, const char *pMode, FILE *pStream)
|
||||
{
|
||||
WCHAR* wPath = mz_utf8z_to_widechar(pPath);
|
||||
WCHAR* wMode = mz_utf8z_to_widechar(pMode);
|
||||
FILE* pFile = NULL;
|
||||
errno_t err = _wfreopen_s(&pFile, wPath, wMode, pStream);
|
||||
free(wPath);
|
||||
free(wMode);
|
||||
return err ? NULL : pFile;
|
||||
FILE *pFile = NULL;
|
||||
if (freopen_s(&pFile, pPath, pMode, pStream))
|
||||
return NULL;
|
||||
return pFile;
|
||||
}
|
||||
|
||||
static int mz_stat64(const char *path, struct __stat64 *buffer)
|
||||
{
|
||||
WCHAR* wPath = mz_utf8z_to_widechar(path);
|
||||
int res = _wstat64(wPath, buffer);
|
||||
free(wPath);
|
||||
return res;
|
||||
}
|
||||
|
||||
#ifndef MINIZ_NO_TIME
|
||||
#include <sys/utime.h>
|
||||
#endif
|
||||
@@ -3112,12 +3020,11 @@ static int mz_stat64(const char *path, struct __stat64 *buffer)
|
||||
#define MZ_FTELL64 _ftelli64
|
||||
#define MZ_FSEEK64 _fseeki64
|
||||
#define MZ_FILE_STAT_STRUCT _stat64
|
||||
#define MZ_FILE_STAT mz_stat64
|
||||
#define MZ_FILE_STAT _stat64
|
||||
#define MZ_FFLUSH fflush
|
||||
#define MZ_FREOPEN mz_freopen
|
||||
#define MZ_DELETE_FILE remove
|
||||
|
||||
#elif defined(__MINGW32__) || defined(__WATCOMC__)
|
||||
#elif defined(__MINGW32__)
|
||||
#ifndef MINIZ_NO_TIME
|
||||
#include <sys/utime.h>
|
||||
#endif
|
||||
@@ -3125,14 +3032,13 @@ static int mz_stat64(const char *path, struct __stat64 *buffer)
|
||||
#define MZ_FCLOSE fclose
|
||||
#define MZ_FREAD fread
|
||||
#define MZ_FWRITE fwrite
|
||||
#define MZ_FTELL64 _ftelli64
|
||||
#define MZ_FSEEK64 _fseeki64
|
||||
#define MZ_FILE_STAT_STRUCT stat
|
||||
#define MZ_FILE_STAT stat
|
||||
#define MZ_FTELL64 ftello64
|
||||
#define MZ_FSEEK64 fseeko64
|
||||
#define MZ_FILE_STAT_STRUCT _stat
|
||||
#define MZ_FILE_STAT _stat
|
||||
#define MZ_FFLUSH fflush
|
||||
#define MZ_FREOPEN(f, m, s) freopen(f, m, s)
|
||||
#define MZ_DELETE_FILE remove
|
||||
|
||||
#elif defined(__TINYC__)
|
||||
#ifndef MINIZ_NO_TIME
|
||||
#include <sys/utime.h>
|
||||
@@ -3148,7 +3054,6 @@ static int mz_stat64(const char *path, struct __stat64 *buffer)
|
||||
#define MZ_FFLUSH fflush
|
||||
#define MZ_FREOPEN(f, m, s) freopen(f, m, s)
|
||||
#define MZ_DELETE_FILE remove
|
||||
|
||||
#elif defined(__USE_LARGEFILE64) /* gcc, clang */
|
||||
#ifndef MINIZ_NO_TIME
|
||||
#include <utime.h>
|
||||
@@ -3164,8 +3069,7 @@ static int mz_stat64(const char *path, struct __stat64 *buffer)
|
||||
#define MZ_FFLUSH fflush
|
||||
#define MZ_FREOPEN(p, m, s) freopen64(p, m, s)
|
||||
#define MZ_DELETE_FILE remove
|
||||
|
||||
#elif defined(__APPLE__) || defined(__FreeBSD__)
|
||||
#elif defined(__APPLE__)
|
||||
#ifndef MINIZ_NO_TIME
|
||||
#include <utime.h>
|
||||
#endif
|
||||
@@ -3311,7 +3215,7 @@ struct mz_zip_internal_state_tag
|
||||
mz_zip_array m_sorted_central_dir_offsets;
|
||||
|
||||
/* The flags passed in when the archive is initially opened. */
|
||||
mz_uint32 m_init_flags;
|
||||
uint32_t m_init_flags;
|
||||
|
||||
/* MZ_TRUE if the archive has a zip64 end of central directory headers, etc. */
|
||||
mz_bool m_zip64;
|
||||
@@ -3747,7 +3651,7 @@ static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, mz_uint flag
|
||||
if (((num_this_disk | cdir_disk_index) != 0) && ((num_this_disk != 1) || (cdir_disk_index != 1)))
|
||||
return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK);
|
||||
|
||||
if (cdir_size < (mz_uint64)pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)
|
||||
if (cdir_size < pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)
|
||||
return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
|
||||
|
||||
if ((cdir_ofs + (mz_uint64)cdir_size) > pZip->m_archive_size)
|
||||
@@ -3898,7 +3802,7 @@ static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, mz_uint flag
|
||||
void mz_zip_zero_struct(mz_zip_archive *pZip)
|
||||
{
|
||||
if (pZip)
|
||||
MZ_CLEAR_PTR(pZip);
|
||||
MZ_CLEAR_OBJ(*pZip);
|
||||
}
|
||||
|
||||
static mz_bool mz_zip_reader_end_internal(mz_zip_archive *pZip, mz_bool set_last_error)
|
||||
@@ -4372,7 +4276,7 @@ static mz_bool mz_zip_locate_file_binary_search(mz_zip_archive *pZip, const char
|
||||
const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets;
|
||||
const mz_zip_array *pCentral_dir = &pState->m_central_dir;
|
||||
mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0);
|
||||
const mz_uint32 size = pZip->m_total_files;
|
||||
const uint32_t size = pZip->m_total_files;
|
||||
const mz_uint filename_len = (mz_uint)strlen(pFilename);
|
||||
|
||||
if (pIndex)
|
||||
@@ -4387,7 +4291,7 @@ static mz_bool mz_zip_locate_file_binary_search(mz_zip_archive *pZip, const char
|
||||
while (l <= h)
|
||||
{
|
||||
mz_int64 m = l + ((h - l) >> 1);
|
||||
mz_uint32 file_index = pIndices[(mz_uint32)m];
|
||||
uint32_t file_index = pIndices[(uint32_t)m];
|
||||
|
||||
int comp = mz_zip_filename_compare(pCentral_dir, pCentral_dir_offsets, file_index, pFilename, filename_len);
|
||||
if (!comp)
|
||||
@@ -4480,8 +4384,7 @@ mz_bool mz_zip_reader_locate_file_v2(mz_zip_archive *pZip, const char *pName, co
|
||||
return mz_zip_set_error(pZip, MZ_ZIP_FILE_NOT_FOUND);
|
||||
}
|
||||
|
||||
static
|
||||
mz_bool mz_zip_reader_extract_to_mem_no_alloc1(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size, const mz_zip_archive_file_stat *st)
|
||||
mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size)
|
||||
{
|
||||
int status = TINFL_STATUS_DONE;
|
||||
mz_uint64 needed_size, cur_file_ofs, comp_remaining, out_buf_ofs = 0, read_buf_size, read_buf_ofs = 0, read_buf_avail;
|
||||
@@ -4494,9 +4397,6 @@ mz_bool mz_zip_reader_extract_to_mem_no_alloc1(mz_zip_archive *pZip, mz_uint fil
|
||||
if ((!pZip) || (!pZip->m_pState) || ((buf_size) && (!pBuf)) || ((user_read_buf_size) && (!pUser_read_buf)) || (!pZip->m_pRead))
|
||||
return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
|
||||
|
||||
if (st) {
|
||||
file_stat = *st;
|
||||
} else
|
||||
if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat))
|
||||
return MZ_FALSE;
|
||||
|
||||
@@ -4627,22 +4527,17 @@ mz_bool mz_zip_reader_extract_to_mem_no_alloc1(mz_zip_archive *pZip, mz_uint fil
|
||||
return status == TINFL_STATUS_DONE;
|
||||
}
|
||||
|
||||
mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size)
|
||||
{
|
||||
return mz_zip_reader_extract_to_mem_no_alloc1(pZip, file_index, pBuf, buf_size, flags, pUser_read_buf, user_read_buf_size, NULL);
|
||||
}
|
||||
|
||||
mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size)
|
||||
{
|
||||
mz_uint32 file_index;
|
||||
if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index))
|
||||
return MZ_FALSE;
|
||||
return mz_zip_reader_extract_to_mem_no_alloc1(pZip, file_index, pBuf, buf_size, flags, pUser_read_buf, user_read_buf_size, NULL);
|
||||
return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, pUser_read_buf, user_read_buf_size);
|
||||
}
|
||||
|
||||
mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags)
|
||||
{
|
||||
return mz_zip_reader_extract_to_mem_no_alloc1(pZip, file_index, pBuf, buf_size, flags, NULL, 0, NULL);
|
||||
return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, NULL, 0);
|
||||
}
|
||||
|
||||
mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags)
|
||||
@@ -4652,17 +4547,23 @@ mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFil
|
||||
|
||||
void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags)
|
||||
{
|
||||
mz_zip_archive_file_stat file_stat;
|
||||
mz_uint64 alloc_size;
|
||||
mz_uint64 comp_size, uncomp_size, alloc_size;
|
||||
const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index);
|
||||
void *pBuf;
|
||||
|
||||
if (pSize)
|
||||
*pSize = 0;
|
||||
|
||||
if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat))
|
||||
if (!p)
|
||||
{
|
||||
mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
alloc_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? file_stat.m_comp_size : file_stat.m_uncomp_size;
|
||||
comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS);
|
||||
uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS);
|
||||
|
||||
alloc_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? comp_size : uncomp_size;
|
||||
if (((sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF))
|
||||
{
|
||||
mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR);
|
||||
@@ -4675,7 +4576,7 @@ void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, si
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!mz_zip_reader_extract_to_mem_no_alloc1(pZip, file_index, pBuf, (size_t)alloc_size, flags, NULL, 0, &file_stat))
|
||||
if (!mz_zip_reader_extract_to_mem(pZip, file_index, pBuf, (size_t)alloc_size, flags))
|
||||
{
|
||||
pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);
|
||||
return NULL;
|
||||
@@ -5136,7 +5037,7 @@ size_t mz_zip_reader_extract_iter_read(mz_zip_reader_extract_iter_state* pState,
|
||||
size_t to_copy = MZ_MIN( (buf_size - copied_to_caller), pState->out_blk_remain );
|
||||
|
||||
/* Copy data to caller's buffer */
|
||||
memcpy( (mz_uint8*)pvBuf + copied_to_caller, pWrite_buf_cur, to_copy );
|
||||
memcpy( (uint8_t*)pvBuf + copied_to_caller, pWrite_buf_cur, to_copy );
|
||||
|
||||
#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS
|
||||
/* Perform CRC */
|
||||
@@ -5505,7 +5406,7 @@ handle_failure:
|
||||
mz_bool mz_zip_validate_archive(mz_zip_archive *pZip, mz_uint flags)
|
||||
{
|
||||
mz_zip_internal_state *pState;
|
||||
mz_uint32 i;
|
||||
uint32_t i;
|
||||
|
||||
if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (!pZip->m_pRead))
|
||||
return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
|
||||
@@ -5523,6 +5424,9 @@ mz_bool mz_zip_validate_archive(mz_zip_archive *pZip, mz_uint flags)
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pZip->m_total_files >= MZ_UINT32_MAX)
|
||||
return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE);
|
||||
|
||||
if (pState->m_central_dir.m_size >= MZ_UINT32_MAX)
|
||||
return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE);
|
||||
}
|
||||
@@ -5884,7 +5788,7 @@ mz_bool mz_zip_writer_init_file_v2(mz_zip_archive *pZip, const char *pFilename,
|
||||
mz_uint64 cur_ofs = 0;
|
||||
char buf[4096];
|
||||
|
||||
MZ_CLEAR_ARR(buf);
|
||||
MZ_CLEAR_OBJ(buf);
|
||||
|
||||
do
|
||||
{
|
||||
@@ -6247,7 +6151,7 @@ mz_bool mz_zip_writer_add_mem_ex_v2(mz_zip_archive *pZip, const char *pArchive_n
|
||||
pState->m_zip64 = MZ_TRUE;
|
||||
/*return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); */
|
||||
}
|
||||
if (((mz_uint64)buf_size > 0xFFFFFFFF) || (uncomp_size > 0xFFFFFFFF))
|
||||
if ((buf_size > 0xFFFFFFFF) || (uncomp_size > 0xFFFFFFFF))
|
||||
{
|
||||
pState->m_zip64 = MZ_TRUE;
|
||||
/*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */
|
||||
@@ -6340,7 +6244,7 @@ mz_bool mz_zip_writer_add_mem_ex_v2(mz_zip_archive *pZip, const char *pArchive_n
|
||||
}
|
||||
cur_archive_file_ofs += num_alignment_padding_bytes;
|
||||
|
||||
MZ_CLEAR_ARR(local_dir_header);
|
||||
MZ_CLEAR_OBJ(local_dir_header);
|
||||
|
||||
if (!store_data_uncompressed || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA))
|
||||
{
|
||||
@@ -6490,7 +6394,7 @@ mz_bool mz_zip_writer_add_mem_ex_v2(mz_zip_archive *pZip, const char *pArchive_n
|
||||
mz_bool mz_zip_writer_add_read_buf_callback(mz_zip_archive *pZip, const char *pArchive_name, mz_file_read_func read_callback, void* callback_opaque, mz_uint64 max_size, const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags,
|
||||
const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len)
|
||||
{
|
||||
mz_uint16 gen_flags;
|
||||
mz_uint16 gen_flags = (level_and_flags & MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE) ? 0 : MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR;
|
||||
mz_uint uncomp_crc32 = MZ_CRC32_INIT, level, num_alignment_padding_bytes;
|
||||
mz_uint16 method = 0, dos_time = 0, dos_date = 0, ext_attributes = 0;
|
||||
mz_uint64 local_dir_header_ofs, cur_archive_file_ofs = pZip->m_archive_size, uncomp_size = 0, comp_size = 0;
|
||||
@@ -6502,15 +6406,13 @@ mz_bool mz_zip_writer_add_read_buf_callback(mz_zip_archive *pZip, const char *pA
|
||||
mz_zip_internal_state *pState;
|
||||
mz_uint64 file_ofs = 0, cur_archive_header_file_ofs;
|
||||
|
||||
if (!(level_and_flags & MZ_ZIP_FLAG_ASCII_FILENAME))
|
||||
gen_flags |= MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8;
|
||||
|
||||
if ((int)level_and_flags < 0)
|
||||
level_and_flags = MZ_DEFAULT_LEVEL;
|
||||
level = level_and_flags & 0xF;
|
||||
|
||||
gen_flags = (level_and_flags & MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE) ? 0 : MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR;
|
||||
|
||||
if (!(level_and_flags & MZ_ZIP_FLAG_ASCII_FILENAME))
|
||||
gen_flags |= MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8;
|
||||
|
||||
/* Sanity checks */
|
||||
if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION))
|
||||
return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
|
||||
@@ -6595,7 +6497,7 @@ mz_bool mz_zip_writer_add_read_buf_callback(mz_zip_archive *pZip, const char *pA
|
||||
method = MZ_DEFLATED;
|
||||
}
|
||||
|
||||
MZ_CLEAR_ARR(local_dir_header);
|
||||
MZ_CLEAR_OBJ(local_dir_header);
|
||||
if (pState->m_zip64)
|
||||
{
|
||||
if (max_size >= MZ_UINT32_MAX || local_dir_header_ofs >= MZ_UINT32_MAX)
|
||||
@@ -6899,7 +6801,7 @@ mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name,
|
||||
}
|
||||
#endif /* #ifndef MINIZ_NO_STDIO */
|
||||
|
||||
static mz_bool mz_zip_writer_update_zip64_extension_block(mz_zip_array *pNew_ext, mz_zip_archive *pZip, const mz_uint8 *pExt, mz_uint32 ext_len, mz_uint64 *pComp_size, mz_uint64 *pUncomp_size, mz_uint64 *pLocal_header_ofs, mz_uint32 *pDisk_start)
|
||||
static mz_bool mz_zip_writer_update_zip64_extension_block(mz_zip_array *pNew_ext, mz_zip_archive *pZip, const mz_uint8 *pExt, uint32_t ext_len, mz_uint64 *pComp_size, mz_uint64 *pUncomp_size, mz_uint64 *pLocal_header_ofs, mz_uint32 *pDisk_start)
|
||||
{
|
||||
/* + 64 should be enough for any new zip64 data */
|
||||
if (!mz_zip_array_reserve(pZip, pNew_ext, ext_len + 64, MZ_FALSE))
|
||||
@@ -7215,10 +7117,10 @@ mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *
|
||||
if (pZip->m_pState->m_zip64)
|
||||
{
|
||||
/* dest is zip64, so upgrade the data descriptor */
|
||||
const mz_uint8 *pSrc_descriptor = (const mz_uint8 *)pBuf + (has_id ? sizeof(mz_uint32) : 0);
|
||||
const mz_uint32 src_crc32 = MZ_READ_LE32(pSrc_descriptor);
|
||||
const mz_uint64 src_comp_size = MZ_READ_LE32(pSrc_descriptor + sizeof(mz_uint32));
|
||||
const mz_uint64 src_uncomp_size = MZ_READ_LE32(pSrc_descriptor + 2*sizeof(mz_uint32));
|
||||
const mz_uint32 *pSrc_descriptor = (const mz_uint32 *)((const mz_uint8 *)pBuf + (has_id ? sizeof(mz_uint32) : 0));
|
||||
const mz_uint32 src_crc32 = pSrc_descriptor[0];
|
||||
const mz_uint64 src_comp_size = pSrc_descriptor[1];
|
||||
const mz_uint64 src_uncomp_size = pSrc_descriptor[2];
|
||||
|
||||
mz_write_le32((mz_uint8 *)pBuf, MZ_ZIP_DATA_DESCRIPTOR_ID);
|
||||
mz_write_le32((mz_uint8 *)pBuf + sizeof(mz_uint32) * 1, src_crc32);
|
||||
@@ -7354,7 +7256,7 @@ mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip)
|
||||
|
||||
if (pState->m_zip64)
|
||||
{
|
||||
if ((mz_uint64)pState->m_central_dir.m_size >= MZ_UINT32_MAX)
|
||||
if ((pZip->m_total_files > MZ_UINT32_MAX) || (pState->m_central_dir.m_size >= MZ_UINT32_MAX))
|
||||
return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES);
|
||||
}
|
||||
else
|
||||
@@ -7382,7 +7284,7 @@ mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip)
|
||||
/* Write zip64 end of central directory header */
|
||||
mz_uint64 rel_ofs_to_zip64_ecdr = pZip->m_archive_size;
|
||||
|
||||
MZ_CLEAR_ARR(hdr);
|
||||
MZ_CLEAR_OBJ(hdr);
|
||||
MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDH_SIG_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG);
|
||||
MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE - sizeof(mz_uint32) - sizeof(mz_uint64));
|
||||
MZ_WRITE_LE16(hdr + MZ_ZIP64_ECDH_VERSION_MADE_BY_OFS, 0x031E); /* TODO: always Unix */
|
||||
@@ -7397,7 +7299,7 @@ mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip)
|
||||
pZip->m_archive_size += MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE;
|
||||
|
||||
/* Write zip64 end of central directory locator */
|
||||
MZ_CLEAR_ARR(hdr);
|
||||
MZ_CLEAR_OBJ(hdr);
|
||||
MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDL_SIG_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG);
|
||||
MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS, rel_ofs_to_zip64_ecdr);
|
||||
MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS, 1);
|
||||
@@ -7408,7 +7310,7 @@ mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip)
|
||||
}
|
||||
|
||||
/* Write end of central directory record */
|
||||
MZ_CLEAR_ARR(hdr);
|
||||
MZ_CLEAR_OBJ(hdr);
|
||||
MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_SIG_OFS, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG);
|
||||
MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, MZ_MIN(MZ_UINT16_MAX, pZip->m_total_files));
|
||||
MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS, MZ_MIN(MZ_UINT16_MAX, pZip->m_total_files));
|
||||
@@ -7724,9 +7626,7 @@ const char *mz_zip_get_error_string(mz_zip_error mz_err)
|
||||
case MZ_ZIP_VALIDATION_FAILED:
|
||||
return "validation failed";
|
||||
case MZ_ZIP_WRITE_CALLBACK_FAILED:
|
||||
return "write callback failed";
|
||||
case MZ_ZIP_TOTAL_ERRORS:
|
||||
return "total errors";
|
||||
return "write calledback failed";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
#ifndef MINIZ_EXPORT
|
||||
#define MINIZ_EXPORT
|
||||
#endif
|
||||
/* miniz.c 3.0.0 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing
|
||||
/* miniz.c 2.2.0 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing
|
||||
See "unlicense" statement at the end of this file.
|
||||
Rich Geldreich <richgel99@gmail.com>, last updated Oct. 13, 2013
|
||||
Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: http://www.ietf.org/rfc/rfc1951.txt
|
||||
@@ -118,7 +116,7 @@
|
||||
|
||||
|
||||
/* Defines to completely disable specific portions of miniz.c:
|
||||
If all macros here are defined the only functionality remaining will be CRC-32 and adler-32. */
|
||||
If all macros here are defined the only functionality remaining will be CRC-32, adler-32, tinfl, and tdefl. */
|
||||
|
||||
/* Define MINIZ_NO_STDIO to disable all usage and any functions which rely on stdio for file I/O. */
|
||||
/*#define MINIZ_NO_STDIO */
|
||||
@@ -128,12 +126,6 @@
|
||||
/* The current downside is the times written to your archives will be from 1979. */
|
||||
/*#define MINIZ_NO_TIME */
|
||||
|
||||
/* Define MINIZ_NO_DEFLATE_APIS to disable all compression API's. */
|
||||
/*#define MINIZ_NO_DEFLATE_APIS */
|
||||
|
||||
/* Define MINIZ_NO_INFLATE_APIS to disable all decompression API's. */
|
||||
/*#define MINIZ_NO_INFLATE_APIS */
|
||||
|
||||
/* Define MINIZ_NO_ARCHIVE_APIS to disable all ZIP archive API's. */
|
||||
/*#define MINIZ_NO_ARCHIVE_APIS */
|
||||
|
||||
@@ -152,14 +144,6 @@
|
||||
functions (such as tdefl_compress_mem_to_heap() and tinfl_decompress_mem_to_heap()) won't work. */
|
||||
/*#define MINIZ_NO_MALLOC */
|
||||
|
||||
#ifdef MINIZ_NO_INFLATE_APIS
|
||||
//#define MINIZ_NO_ARCHIVE_APIS
|
||||
#endif
|
||||
|
||||
#ifdef MINIZ_NO_DEFLATE_APIS
|
||||
#define MINIZ_NO_ARCHIVE_WRITING_APIS
|
||||
#endif
|
||||
|
||||
#if defined(__TINYC__) && (defined(__linux) || defined(__linux__))
|
||||
/* TODO: Work around "error: include file 'sys\utime.h' when compiling with tcc on Linux */
|
||||
#define MINIZ_NO_TIME
|
||||
@@ -178,40 +162,18 @@
|
||||
#define MINIZ_X86_OR_X64_CPU 0
|
||||
#endif
|
||||
|
||||
/* Set MINIZ_LITTLE_ENDIAN only if not set */
|
||||
#if !defined(MINIZ_LITTLE_ENDIAN)
|
||||
#if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__)
|
||||
|
||||
#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
|
||||
#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU
|
||||
/* Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian. */
|
||||
#define MINIZ_LITTLE_ENDIAN 1
|
||||
#else
|
||||
#define MINIZ_LITTLE_ENDIAN 0
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#if MINIZ_X86_OR_X64_CPU
|
||||
#define MINIZ_LITTLE_ENDIAN 1
|
||||
#else
|
||||
#define MINIZ_LITTLE_ENDIAN 0
|
||||
#endif
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Using unaligned loads and stores causes errors when using UBSan */
|
||||
#if defined(__has_feature)
|
||||
#if __has_feature(undefined_behavior_sanitizer)
|
||||
#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES only if not set */
|
||||
#if !defined(MINIZ_USE_UNALIGNED_LOADS_AND_STORES)
|
||||
#if MINIZ_X86_OR_X64_CPU
|
||||
/* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient integer loads and stores from unaligned addresses. */
|
||||
#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0
|
||||
#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1
|
||||
#define MINIZ_UNALIGNED_USE_MEMCPY
|
||||
#else
|
||||
#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0
|
||||
@@ -275,10 +237,10 @@ enum
|
||||
MZ_DEFAULT_COMPRESSION = -1
|
||||
};
|
||||
|
||||
#define MZ_VERSION "11.0.1"
|
||||
#define MZ_VERNUM 0xB001
|
||||
#define MZ_VER_MAJOR 11
|
||||
#define MZ_VER_MINOR 1
|
||||
#define MZ_VERSION "10.2.0"
|
||||
#define MZ_VERNUM 0xA100
|
||||
#define MZ_VER_MAJOR 10
|
||||
#define MZ_VER_MINOR 2
|
||||
#define MZ_VER_REVISION 0
|
||||
#define MZ_VER_SUBREVISION 0
|
||||
|
||||
@@ -343,8 +305,6 @@ typedef mz_stream *mz_streamp;
|
||||
/* Returns the version string of miniz.c. */
|
||||
MINIZ_EXPORT const char *mz_version(void);
|
||||
|
||||
#ifndef MINIZ_NO_DEFLATE_APIS
|
||||
|
||||
/* mz_deflateInit() initializes a compressor with default options: */
|
||||
/* Parameters: */
|
||||
/* pStream must point to an initialized mz_stream struct. */
|
||||
@@ -397,10 +357,6 @@ MINIZ_EXPORT int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const u
|
||||
/* mz_compressBound() returns a (very) conservative upper bound on the amount of data that could be generated by calling mz_compress(). */
|
||||
MINIZ_EXPORT mz_ulong mz_compressBound(mz_ulong source_len);
|
||||
|
||||
#endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/
|
||||
|
||||
#ifndef MINIZ_NO_INFLATE_APIS
|
||||
|
||||
/* Initializes a decompressor. */
|
||||
MINIZ_EXPORT int mz_inflateInit(mz_streamp pStream);
|
||||
|
||||
@@ -434,7 +390,6 @@ MINIZ_EXPORT int mz_inflateEnd(mz_streamp pStream);
|
||||
/* Returns MZ_OK on success, or one of the error codes from mz_inflate() on failure. */
|
||||
MINIZ_EXPORT int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len);
|
||||
MINIZ_EXPORT int mz_uncompress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong *pSource_len);
|
||||
#endif /*#ifndef MINIZ_NO_INFLATE_APIS*/
|
||||
|
||||
/* Returns a string description of the specified error code, or NULL if the error code is invalid. */
|
||||
MINIZ_EXPORT const char *mz_error(int err);
|
||||
@@ -485,8 +440,6 @@ typedef void *const voidpc;
|
||||
#define free_func mz_free_func
|
||||
#define internal_state mz_internal_state
|
||||
#define z_stream mz_stream
|
||||
|
||||
#ifndef MINIZ_NO_DEFLATE_APIS
|
||||
#define deflateInit mz_deflateInit
|
||||
#define deflateInit2 mz_deflateInit2
|
||||
#define deflateReset mz_deflateReset
|
||||
@@ -496,9 +449,6 @@ typedef void *const voidpc;
|
||||
#define compress mz_compress
|
||||
#define compress2 mz_compress2
|
||||
#define compressBound mz_compressBound
|
||||
#endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/
|
||||
|
||||
#ifndef MINIZ_NO_INFLATE_APIS
|
||||
#define inflateInit mz_inflateInit
|
||||
#define inflateInit2 mz_inflateInit2
|
||||
#define inflateReset mz_inflateReset
|
||||
@@ -506,8 +456,6 @@ typedef void *const voidpc;
|
||||
#define inflateEnd mz_inflateEnd
|
||||
#define uncompress mz_uncompress
|
||||
#define uncompress2 mz_uncompress2
|
||||
#endif /*#ifndef MINIZ_NO_INFLATE_APIS*/
|
||||
|
||||
#define crc32 mz_crc32
|
||||
#define adler32 mz_adler32
|
||||
#define MAX_WBITS 15
|
||||
@@ -571,8 +519,7 @@ typedef int mz_bool;
|
||||
#ifdef MINIZ_NO_TIME
|
||||
typedef struct mz_dummy_time_t_tag
|
||||
{
|
||||
mz_uint32 m_dummy1;
|
||||
mz_uint32 m_dummy2;
|
||||
int m_dummy;
|
||||
} mz_dummy_time_t;
|
||||
#define MZ_TIME_T mz_dummy_time_t
|
||||
#else
|
||||
@@ -594,8 +541,6 @@ typedef struct mz_dummy_time_t_tag
|
||||
#define MZ_MAX(a, b) (((a) > (b)) ? (a) : (b))
|
||||
#define MZ_MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||
#define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj))
|
||||
#define MZ_CLEAR_ARR(obj) memset((obj), 0, sizeof(obj))
|
||||
#define MZ_CLEAR_PTR(obj) memset((obj), 0, sizeof(*obj))
|
||||
|
||||
#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN
|
||||
#define MZ_READ_LE16(p) *((const mz_uint16 *)(p))
|
||||
@@ -632,8 +577,6 @@ extern MINIZ_EXPORT void *miniz_def_realloc_func(void *opaque, void *address, si
|
||||
#pragma once
|
||||
|
||||
|
||||
#ifndef MINIZ_NO_DEFLATE_APIS
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@@ -821,14 +764,10 @@ MINIZ_EXPORT void tdefl_compressor_free(tdefl_compressor *pComp);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/
|
||||
#pragma once
|
||||
|
||||
/* ------------------- Low-level Decompression API Definitions */
|
||||
|
||||
#ifndef MINIZ_NO_INFLATE_APIS
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@@ -937,6 +876,12 @@ enum
|
||||
TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
mz_uint8 m_code_size[TINFL_MAX_HUFF_SYMBOLS_0];
|
||||
mz_int16 m_look_up[TINFL_FAST_LOOKUP_SIZE], m_tree[TINFL_MAX_HUFF_SYMBOLS_0 * 2];
|
||||
} tinfl_huff_table;
|
||||
|
||||
#if MINIZ_HAS_64BIT_REGISTERS
|
||||
#define TINFL_USE_64BIT_BITBUF 1
|
||||
#else
|
||||
@@ -956,13 +901,7 @@ struct tinfl_decompressor_tag
|
||||
mz_uint32 m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, m_check_adler32, m_dist, m_counter, m_num_extra, m_table_sizes[TINFL_MAX_HUFF_TABLES];
|
||||
tinfl_bit_buf_t m_bit_buf;
|
||||
size_t m_dist_from_out_buf_start;
|
||||
mz_int16 m_look_up[TINFL_MAX_HUFF_TABLES][TINFL_FAST_LOOKUP_SIZE];
|
||||
mz_int16 m_tree_0[TINFL_MAX_HUFF_SYMBOLS_0 * 2];
|
||||
mz_int16 m_tree_1[TINFL_MAX_HUFF_SYMBOLS_1 * 2];
|
||||
mz_int16 m_tree_2[TINFL_MAX_HUFF_SYMBOLS_2 * 2];
|
||||
mz_uint8 m_code_size_0[TINFL_MAX_HUFF_SYMBOLS_0];
|
||||
mz_uint8 m_code_size_1[TINFL_MAX_HUFF_SYMBOLS_1];
|
||||
mz_uint8 m_code_size_2[TINFL_MAX_HUFF_SYMBOLS_2];
|
||||
tinfl_huff_table m_tables[TINFL_MAX_HUFF_TABLES];
|
||||
mz_uint8 m_raw_header[4], m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137];
|
||||
};
|
||||
|
||||
@@ -970,8 +909,6 @@ struct tinfl_decompressor_tag
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /*#ifndef MINIZ_NO_INFLATE_APIS*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
@@ -1005,6 +942,10 @@ typedef struct
|
||||
mz_uint16 m_bit_flag;
|
||||
mz_uint16 m_method;
|
||||
|
||||
#ifndef MINIZ_NO_TIME
|
||||
MZ_TIME_T m_time;
|
||||
#endif
|
||||
|
||||
/* CRC-32 of uncompressed data. */
|
||||
mz_uint32 m_crc32;
|
||||
|
||||
@@ -1041,11 +982,6 @@ typedef struct
|
||||
/* Guaranteed to be zero terminated, may be truncated to fit. */
|
||||
char m_comment[MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE];
|
||||
|
||||
#ifdef MINIZ_NO_TIME
|
||||
MZ_TIME_T m_padding;
|
||||
#else
|
||||
MZ_TIME_T m_time;
|
||||
#endif
|
||||
} mz_zip_archive_file_stat;
|
||||
|
||||
typedef size_t (*mz_file_read_func)(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n);
|
||||
@@ -1157,7 +1093,9 @@ typedef struct
|
||||
mz_uint flags;
|
||||
|
||||
int status;
|
||||
|
||||
#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS
|
||||
mz_uint file_crc32;
|
||||
#endif
|
||||
mz_uint64 read_buf_size, read_buf_ofs, read_buf_avail, comp_remaining, out_buf_ofs, cur_file_ofs;
|
||||
mz_zip_archive_file_stat file_stat;
|
||||
void *pRead_buf;
|
||||
@@ -1167,12 +1105,6 @@ typedef struct
|
||||
|
||||
tinfl_decompressor inflator;
|
||||
|
||||
#ifdef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS
|
||||
mz_uint padding;
|
||||
#else
|
||||
mz_uint file_crc32;
|
||||
#endif
|
||||
|
||||
} mz_zip_reader_extract_iter_state;
|
||||
|
||||
/* -------- ZIP reading */
|
||||
@@ -1296,9 +1228,9 @@ MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_cfile(mz_zip_archive *pZip, c
|
||||
/* TODO */
|
||||
typedef void *mz_zip_streaming_extract_state_ptr;
|
||||
mz_zip_streaming_extract_state_ptr mz_zip_streaming_extract_begin(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags);
|
||||
mz_uint64 mz_zip_streaming_extract_get_size(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState);
|
||||
mz_uint64 mz_zip_streaming_extract_get_cur_ofs(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState);
|
||||
mz_bool mz_zip_streaming_extract_seek(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState, mz_uint64 new_ofs);
|
||||
uint64_t mz_zip_streaming_extract_get_size(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState);
|
||||
uint64_t mz_zip_streaming_extract_get_cur_ofs(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState);
|
||||
mz_bool mz_zip_streaming_extract_seek(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState, uint64_t new_ofs);
|
||||
size_t mz_zip_streaming_extract_read(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState, void *pBuf, size_t buf_size);
|
||||
mz_bool mz_zip_streaming_extract_end(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState);
|
||||
#endif
|
||||
@@ -1312,9 +1244,7 @@ MINIZ_EXPORT mz_bool mz_zip_validate_archive(mz_zip_archive *pZip, mz_uint flags
|
||||
|
||||
/* Misc utils/helpers, valid for ZIP reading or writing */
|
||||
MINIZ_EXPORT mz_bool mz_zip_validate_mem_archive(const void *pMem, size_t size, mz_uint flags, mz_zip_error *pErr);
|
||||
#ifndef MINIZ_NO_STDIO
|
||||
MINIZ_EXPORT mz_bool mz_zip_validate_file_archive(const char *pFilename, mz_uint flags, mz_zip_error *pErr);
|
||||
#endif
|
||||
|
||||
/* Universal end function - calls either mz_zip_reader_end() or mz_zip_writer_end(). */
|
||||
MINIZ_EXPORT mz_bool mz_zip_end(mz_zip_archive *pZip);
|
||||
@@ -1388,7 +1318,7 @@ MINIZ_EXPORT mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_
|
||||
/* An archive must be manually finalized by calling this function for it to be valid. */
|
||||
MINIZ_EXPORT mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip);
|
||||
|
||||
/* Finalizes a heap archive, returning a pointer to the heap block and its size. */
|
||||
/* Finalizes a heap archive, returning a poiner to the heap block and its size. */
|
||||
/* The heap block will be allocated using the mz_zip_archive's alloc/realloc callbacks. */
|
||||
MINIZ_EXPORT mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **ppBuf, size_t *pSize);
|
||||
|
||||
@@ -1405,13 +1335,11 @@ MINIZ_EXPORT mz_bool mz_zip_writer_end(mz_zip_archive *pZip);
|
||||
MINIZ_EXPORT mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags);
|
||||
MINIZ_EXPORT mz_bool mz_zip_add_mem_to_archive_file_in_place_v2(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_zip_error *pErr);
|
||||
|
||||
#ifndef MINIZ_NO_STDIO
|
||||
/* Reads a single file from an archive into a heap block. */
|
||||
/* If pComment is not NULL, only the file with the specified comment will be extracted. */
|
||||
/* Returns NULL on failure. */
|
||||
MINIZ_EXPORT void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags);
|
||||
MINIZ_EXPORT void *mz_zip_extract_archive_file_to_heap_v2(const char *pZip_filename, const char *pArchive_name, const char *pComment, size_t *pSize, mz_uint flags, mz_zip_error *pErr);
|
||||
#endif
|
||||
|
||||
#endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS */
|
||||
|
||||
@@ -1,235 +0,0 @@
|
||||
## Changelog
|
||||
|
||||
### 3.0.1
|
||||
|
||||
- Fix compilation error with MINIZ_USE_UNALIGNED_LOADS_AND_STORES=1
|
||||
|
||||
### 3.0.0
|
||||
|
||||
- Reduce memory usage for inflate. This changes `struct tinfl_decompressor_tag` and therefore requires a major version bump (breaks ABI compatibility)
|
||||
- Add padding to structures so it continues to work if features differ. This also changes some structures
|
||||
- Use _ftelli64, _fseeki64 and stat with MinGW32 and OpenWatcom
|
||||
- Fix varios warnings with OpenWatcom compiler
|
||||
- Avoid using unaligned memory access in UBSan builds
|
||||
- Set MINIZ_LITTLE_ENDIAN only if not set
|
||||
- Add MINIZ_NO_DEFLATE_APIS and MINIZ_NO_INFLATE_APIS
|
||||
- Fix use of uninitialized memory in tinfl_decompress_mem_to_callback()
|
||||
- Use wfopen on windows
|
||||
- Use _wstat64 instead _stat64 on windows
|
||||
- Use level_and_flags after MZ_DEFAULT_COMPRESSION has been handled
|
||||
- Improve endianess detection
|
||||
- Don't use unaligned stores and loads per default
|
||||
- Fix function declaration if MINIZ_NO_STDIO is used
|
||||
- Fix MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8 not being set
|
||||
- Remove total files check (its 32-bit uint)
|
||||
- tinfl_decompress: avoid NULL ptr arithmetic UB
|
||||
- miniz_zip: fix mz_zip_reader_extract_to_heap to read correct sizes
|
||||
- Eliminate 64-bit operations on 32-bit machines
|
||||
- Disable treating warnings as error with MSVC
|
||||
- Disable building shared lib via CMake by default
|
||||
- Fixed alignment problems on MacOS
|
||||
- Fixed get error string for MZ_ZIP_TOTAL_ERRORS
|
||||
- Write correct FLEVEL 2-bit value in zlib header
|
||||
- miniz.pc.in: fix include path not containing the "miniz" suffix
|
||||
- Fix compatibility with FreeBSD
|
||||
- pkg-config tweaks
|
||||
- Fix integer overflow in header corruption check
|
||||
- Fix some warnings
|
||||
- tdefl_compress_normal: Avoid NULL ptr arithmetic UB
|
||||
- replace use of stdint.h types with mz_ variants
|
||||
|
||||
|
||||
### 2.2.0
|
||||
|
||||
- Fix examples with amalgamation
|
||||
- Modified cmake script to support shared library mode and find_package
|
||||
- Fix for misleading doc comment on `mz_zip_reader_init_cfile` function
|
||||
- Add include location tolerance and stop forcing `_GNU_SOURCE`
|
||||
- Fix: mz_zip_reader_locate_file_v2 returns an mz_bool
|
||||
- Fix large file system checks
|
||||
- Add #elif to enable an external mz_crc32() to be linked in
|
||||
- Write with dynamic size (size of file/data to be added not known before adding)
|
||||
- Added uncompress2 for zlib compatibility
|
||||
- Add support for building as a Meson subproject
|
||||
- Added OSSFuzz support; Integrate with CIFuzz
|
||||
- Add pkg-config file
|
||||
- Fixed use-of-uninitialized value msan error when copying dist bytes with no output bytes written.
|
||||
- mz_zip_validate_file(): fix memory leak on errors
|
||||
- Fixed MSAN use-of-uninitialized in tinfl_decompress when invalid dist is decoded. In this instance dist was 31 which s_dist_base translates as 0
|
||||
- Add flag to set (compressed) size in local file header
|
||||
- avoid use of uninitialized value in tdefl_record_literal
|
||||
|
||||
### 2.1.0
|
||||
|
||||
- More instances of memcpy instead of cast and use memcpy per default
|
||||
- Remove inline for c90 support
|
||||
- New function to read files via callback functions when adding them
|
||||
- Fix out of bounds read while reading Zip64 extended information
|
||||
- guard memcpy when n == 0 because buffer may be NULL
|
||||
- Implement inflateReset() function
|
||||
- Move comp/decomp alloc/free prototypes under guarding #ifndef MZ_NO_MALLOC
|
||||
- Fix large file support under Windows
|
||||
- Don't warn if _LARGEFILE64_SOURCE is not defined to 1
|
||||
- Fixes for MSVC warnings
|
||||
- Remove check that path of file added to archive contains ':' or '\'
|
||||
- Add !defined check on MINIZ_USE_ALIGNED_LOADS_AND_STORES
|
||||
|
||||
### 2.0.8
|
||||
|
||||
- Remove unimplemented functions (mz_zip_locate_file and mz_zip_locate_file_v2)
|
||||
- Add license, changelog, readme and example files to release zip
|
||||
- Fix heap overflow to user buffer in tinfl_status tinfl_decompress
|
||||
- Fix corrupt archive if uncompressed file smaller than 4 byte and the file is added by mz_zip_writer_add_mem*
|
||||
|
||||
### 2.0.7
|
||||
|
||||
- Removed need in C++ compiler in cmake build
|
||||
- Fixed a lot of uninitialized value errors found with Valgrind by memsetting m_dict to 0 in tdefl_init
|
||||
- Fix resource leak in mz_zip_reader_init_file_v2
|
||||
- Fix assert with mz_zip_writer_add_mem* w/MZ_DEFAULT_COMPRESSION
|
||||
- cmake build: install library and headers
|
||||
- Remove _LARGEFILE64_SOURCE requirement from apple defines for large files
|
||||
|
||||
### 2.0.6
|
||||
|
||||
- Improve MZ_ZIP_FLAG_WRITE_ZIP64 documentation
|
||||
- Remove check for cur_archive_file_ofs > UINT_MAX because cur_archive_file_ofs is not used after this point
|
||||
- Add cmake debug configuration
|
||||
- Fix PNG height when creating png files
|
||||
- Add "iterative" file extraction method based on mz_zip_reader_extract_to_callback.
|
||||
- Option to use memcpy for unaligned data access
|
||||
- Define processor/arch macros as zero if not set to one
|
||||
|
||||
### 2.0.4/2.0.5
|
||||
|
||||
- Fix compilation with the various omission compile definitions
|
||||
|
||||
### 2.0.3
|
||||
|
||||
- Fix GCC/clang compile warnings
|
||||
- Added callback for periodic flushes (for ZIP file streaming)
|
||||
- Use UTF-8 for file names in ZIP files per default
|
||||
|
||||
### 2.0.2
|
||||
|
||||
- Fix source backwards compatibility with 1.x
|
||||
- Fix a ZIP bit not being set correctly
|
||||
|
||||
### 2.0.1
|
||||
|
||||
- Added some tests
|
||||
- Added CI
|
||||
- Make source code ANSI C compatible
|
||||
|
||||
### 2.0.0 beta
|
||||
|
||||
- Matthew Sitton merged miniz 1.x to Rich Geldreich's vogl ZIP64 changes. Miniz is now licensed as MIT since the vogl code base is MIT licensed
|
||||
- Miniz is now split into several files
|
||||
- Miniz does now not seek backwards when creating ZIP files. That is the ZIP files can be streamed
|
||||
- Miniz automatically switches to the ZIP64 format when the created ZIP files goes over ZIP file limits
|
||||
- Similar to [SQLite](https://www.sqlite.org/amalgamation.html) the Miniz source code is amalgamated into one miniz.c/miniz.h pair in a build step (amalgamate.sh). Please use miniz.c/miniz.h in your projects
|
||||
- Miniz 2 is only source back-compatible with miniz 1.x. It breaks binary compatibility because structures changed
|
||||
|
||||
### v1.16 BETA Oct 19, 2013
|
||||
|
||||
Still testing, this release is downloadable from [here](http://www.tenacioussoftware.com/miniz_v116_beta_r1.7z). Two key inflator-only robustness and streaming related changes. Also merged in tdefl_compressor_alloc(), tdefl_compressor_free() helpers to make script bindings easier for rustyzip. I would greatly appreciate any help with testing or any feedback.
|
||||
|
||||
The inflator in raw (non-zlib) mode is now usable on gzip or similar streams that have a bunch of bytes following the raw deflate data (problem discovered by rustyzip author williamw520). This version should never read beyond the last byte of the raw deflate data independent of how many bytes you pass into the input buffer.
|
||||
|
||||
The inflator now has a new failure status TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS (-4). Previously, if the inflator was starved of bytes and could not make progress (because the input buffer was empty and the caller did not set the TINFL_FLAG_HAS_MORE_INPUT flag - say on truncated or corrupted compressed data stream) it would append all 0's to the input and try to soldier on. This is scary behavior if the caller didn't know when to stop accepting output (because it didn't know how much uncompressed data was expected, or didn't enforce a sane maximum). v1.16 will instead return TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS immediately if it needs 1 or more bytes to make progress, the input buf is empty, and the caller has indicated that no more input is available. This is a "soft" failure, so you can call the inflator again with more input and it will try to continue, or you can give up and fail. This could be very useful in network streaming scenarios.
|
||||
|
||||
- The inflator coroutine func. is subtle and complex so I'm being cautious about this release. I would greatly appreciate any help with testing or any feedback.
|
||||
I feel good about these changes, and they've been through several hours of automated testing, but they will probably not fix anything for the majority of prev. users so I'm
|
||||
going to mark this release as beta for a few weeks and continue testing it at work/home on various things.
|
||||
- The inflator in raw (non-zlib) mode is now usable on gzip or similar data streams that have a bunch of bytes following the raw deflate data (problem discovered by rustyzip author williamw520).
|
||||
This version should *never* read beyond the last byte of the raw deflate data independent of how many bytes you pass into the input buffer. This issue was caused by the various Huffman bitbuffer lookahead optimizations, and
|
||||
would not be an issue if the caller knew and enforced the precise size of the raw compressed data *or* if the compressed data was in zlib format (i.e. always followed by the byte aligned zlib adler32).
|
||||
So in other words, you can now call the inflator on deflate streams that are followed by arbitrary amounts of data and it's guaranteed that decompression will stop exactly on the last byte.
|
||||
- The inflator now has a new failure status: TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS (-4). Previously, if the inflator was starved of bytes and could not make progress (because the input buffer was empty and the
|
||||
caller did not set the TINFL_FLAG_HAS_MORE_INPUT flag - say on truncated or corrupted compressed data stream) it would append all 0's to the input and try to soldier on.
|
||||
This is scary, because in the worst case, I believe it was possible for the prev. inflator to start outputting large amounts of literal data. If the caller didn't know when to stop accepting output
|
||||
(because it didn't know how much uncompressed data was expected, or didn't enforce a sane maximum) it could continue forever. v1.16 cannot fall into this failure mode, instead it'll return
|
||||
TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS immediately if it needs 1 or more bytes to make progress, the input buf is empty, and the caller has indicated that no more input is available. This is a "soft"
|
||||
failure, so you can call the inflator again with more input and it will try to continue, or you can give up and fail. This could be very useful in network streaming scenarios.
|
||||
- Added documentation to all the tinfl return status codes, fixed miniz_tester so it accepts double minus params for Linux, tweaked example1.c, added a simple "follower bytes" test to miniz_tester.cpp.
|
||||
### v1.15 r4 STABLE - Oct 13, 2013
|
||||
|
||||
Merged over a few very minor bug fixes that I fixed in the zip64 branch. This is downloadable from [here](http://code.google.com/p/miniz/downloads/list) and also in SVN head (as of 10/19/13).
|
||||
|
||||
|
||||
### v1.15 - Oct. 13, 2013
|
||||
|
||||
Interim bugfix release while I work on the next major release with zip64 and streaming compression/decompression support. Fixed the MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY bug (thanks kahmyong.moon@hp.com), which could cause the locate files func to not find files when this flag was specified. Also fixed a bug in mz_zip_reader_extract_to_mem_no_alloc() with user provided read buffers (thanks kymoon). I also merged lots of compiler fixes from various github repo branches and Google Code issue reports. I finally added cmake support (only tested under for Linux so far), compiled and tested with clang v3.3 and gcc 4.6 (under Linux), added defl_write_image_to_png_file_in_memory_ex() (supports Y flipping for OpenGL use, real-time compression), added a new PNG example (example6.c - Mandelbrot), and I added 64-bit file I/O support (stat64(), etc.) for glibc.
|
||||
|
||||
- Critical fix for the MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY bug (thanks kahmyong.moon@hp.com) which could cause locate files to not find files. This bug
|
||||
would only have occurred in earlier versions if you explicitly used this flag, OR if you used mz_zip_extract_archive_file_to_heap() or mz_zip_add_mem_to_archive_file_in_place()
|
||||
(which used this flag). If you can't switch to v1.15 but want to fix this bug, just remove the uses of this flag from both helper funcs (and of course don't use the flag).
|
||||
- Bugfix in mz_zip_reader_extract_to_mem_no_alloc() from kymoon when pUser_read_buf is not NULL and compressed size is > uncompressed size
|
||||
- Fixing mz_zip_reader_extract_*() funcs so they don't try to extract compressed data from directory entries, to account for weird zipfiles which contain zero-size compressed data on dir entries.
|
||||
Hopefully this fix won't cause any issues on weird zip archives, because it assumes the low 16-bits of zip external attributes are DOS attributes (which I believe they always are in practice).
|
||||
- Fixing mz_zip_reader_is_file_a_directory() so it doesn't check the internal attributes, just the filename and external attributes
|
||||
- mz_zip_reader_init_file() - missing MZ_FCLOSE() call if the seek failed
|
||||
- Added cmake support for Linux builds which builds all the examples, tested with clang v3.3 and gcc v4.6.
|
||||
- Clang fix for tdefl_write_image_to_png_file_in_memory() from toffaletti
|
||||
- Merged MZ_FORCEINLINE fix from hdeanclark
|
||||
- Fix <time.h> include before config #ifdef, thanks emil.brink
|
||||
- Added tdefl_write_image_to_png_file_in_memory_ex(): supports Y flipping (super useful for OpenGL apps), and explicit control over the compression level (so you can
|
||||
set it to 1 for real-time compression).
|
||||
- Merged in some compiler fixes from paulharris's github repro.
|
||||
- Retested this build under Windows (VS 2010, including static analysis), tcc 0.9.26, gcc v4.6 and clang v3.3.
|
||||
- Added example6.c, which dumps an image of the mandelbrot set to a PNG file.
|
||||
- Modified example2 to help test the MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY flag more.
|
||||
- In r3: Bugfix to mz_zip_writer_add_file() found during merge: Fix possible src file fclose() leak if alignment bytes+local header file write faiiled
|
||||
- In r4: Minor bugfix to mz_zip_writer_add_from_zip_reader(): Was pushing the wrong central dir header offset, appears harmless in this release, but it became a problem in the zip64 branch
|
||||
|
||||
### v1.14 - May 20, 2012
|
||||
|
||||
(SVN Only) Minor tweaks to get miniz.c compiling with the Tiny C Compiler, added #ifndef MINIZ_NO_TIME guards around utime.h includes. Adding mz_free() function, so the caller can free heap blocks returned by miniz using whatever heap functions it has been configured to use, MSVC specific fixes to use "safe" variants of several functions (localtime_s, fopen_s, freopen_s).
|
||||
|
||||
MinGW32/64 GCC 4.6.1 compiler fixes: added MZ_FORCEINLINE, #include <time.h> (thanks fermtect).
|
||||
|
||||
Compiler specific fixes, some from fermtect. I upgraded to TDM GCC 4.6.1 and now static __forceinline is giving it fits, so I'm changing all usage of __forceinline to MZ_FORCEINLINE and forcing gcc to use __attribute__((__always_inline__)) (and MSVC to use __forceinline). Also various fixes from fermtect for MinGW32: added #include , 64-bit ftell/fseek fixes.
|
||||
|
||||
### v1.13 - May 19, 2012
|
||||
|
||||
From jason@cornsyrup.org and kelwert@mtu.edu - Most importantly, fixed mz_crc32() so it doesn't compute the wrong CRC-32's when mz_ulong is 64-bits. Temporarily/locally slammed in "typedef unsigned long mz_ulong" and re-ran a randomized regression test on ~500k files. Other stuff:
|
||||
|
||||
Eliminated a bunch of warnings when compiling with GCC 32-bit/64. Ran all examples, miniz.c, and tinfl.c through MSVC 2008's /analyze (static analysis) option and fixed all warnings (except for the silly "Use of the comma-operator in a tested expression.." analysis warning, which I purposely use to work around a MSVC compiler warning).
|
||||
|
||||
Created 32-bit and 64-bit Codeblocks projects/workspace. Built and tested Linux executables. The codeblocks workspace is compatible with Linux+Win32/x64. Added miniz_tester solution/project, which is a useful little app derived from LZHAM's tester app that I use as part of the regression test. Ran miniz.c and tinfl.c through another series of regression testing on ~500,000 files and archives. Modified example5.c so it purposely disables a bunch of high-level functionality (MINIZ_NO_STDIO, etc.). (Thanks to corysama for the MINIZ_NO_STDIO bug report.)
|
||||
|
||||
Fix ftell() usage in a few of the examples so they exit with an error on files which are too large (a limitation of the examples, not miniz itself). Fix fail logic handling in mz_zip_add_mem_to_archive_file_in_place() so it always calls mz_zip_writer_finalize_archive() and mz_zip_writer_end(), even if the file add fails.
|
||||
|
||||
- From jason@cornsyrup.org and kelwert@mtu.edu - Fix mz_crc32() so it doesn't compute the wrong CRC-32's when mz_ulong is 64-bit.
|
||||
- Temporarily/locally slammed in "typedef unsigned long mz_ulong" and re-ran a randomized regression test on ~500k files.
|
||||
- Eliminated a bunch of warnings when compiling with GCC 32-bit/64.
|
||||
- Ran all examples, miniz.c, and tinfl.c through MSVC 2008's /analyze (static analysis) option and fixed all warnings (except for the silly
|
||||
"Use of the comma-operator in a tested expression.." analysis warning, which I purposely use to work around a MSVC compiler warning).
|
||||
- Created 32-bit and 64-bit Codeblocks projects/workspace. Built and tested Linux executables. The codeblocks workspace is compatible with Linux+Win32/x64.
|
||||
- Added miniz_tester solution/project, which is a useful little app derived from LZHAM's tester app that I use as part of the regression test.
|
||||
- Ran miniz.c and tinfl.c through another series of regression testing on ~500,000 files and archives.
|
||||
- Modified example5.c so it purposely disables a bunch of high-level functionality (MINIZ_NO_STDIO, etc.). (Thanks to corysama for the MINIZ_NO_STDIO bug report.)
|
||||
- Fix ftell() usage in examples so they exit with an error on files which are too large (a limitation of the examples, not miniz itself).
|
||||
|
||||
### v1.12 - 4/12/12
|
||||
|
||||
More comments, added low-level example5.c, fixed a couple minor level_and_flags issues in the archive API's.
|
||||
level_and_flags can now be set to MZ_DEFAULT_COMPRESSION. Thanks to Bruce Dawson <bruced@valvesoftware.com> for the feedback/bug report.
|
||||
|
||||
### v1.11 - 5/28/11
|
||||
|
||||
Added statement from unlicense.org
|
||||
|
||||
### v1.10 - 5/27/11
|
||||
|
||||
- Substantial compressor optimizations:
|
||||
- Level 1 is now ~4x faster than before. The L1 compressor's throughput now varies between 70-110MB/sec. on a Core i7 (actual throughput varies depending on the type of data, and x64 vs. x86).
|
||||
- Improved baseline L2-L9 compression perf. Also, greatly improved compression perf. issues on some file types.
|
||||
- Refactored the compression code for better readability and maintainability.
|
||||
- Added level 10 compression level (L10 has slightly better ratio than level 9, but could have a potentially large drop in throughput on some files).
|
||||
|
||||
### v1.09 - 5/15/11
|
||||
|
||||
Initial stable release.
|
||||
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
Copyright 2013-2014 RAD Game Tools and Valve Software
|
||||
Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC
|
||||
|
||||
All Rights Reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
@@ -1,105 +0,0 @@
|
||||
// example1.c - Demonstrates miniz.c's compress() and uncompress() functions (same as zlib's).
|
||||
// Public domain, May 15 2011, Rich Geldreich, richgel99@gmail.com. See "unlicense" statement at the end of tinfl.c.
|
||||
#include <stdio.h>
|
||||
#include "miniz.h"
|
||||
typedef unsigned char uint8;
|
||||
typedef unsigned short uint16;
|
||||
typedef unsigned int uint;
|
||||
|
||||
// The string to compress.
|
||||
static const char *s_pStr = "Good morning Dr. Chandra. This is Hal. I am ready for my first lesson." \
|
||||
"Good morning Dr. Chandra. This is Hal. I am ready for my first lesson." \
|
||||
"Good morning Dr. Chandra. This is Hal. I am ready for my first lesson." \
|
||||
"Good morning Dr. Chandra. This is Hal. I am ready for my first lesson." \
|
||||
"Good morning Dr. Chandra. This is Hal. I am ready for my first lesson." \
|
||||
"Good morning Dr. Chandra. This is Hal. I am ready for my first lesson." \
|
||||
"Good morning Dr. Chandra. This is Hal. I am ready for my first lesson.";
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
uint step = 0;
|
||||
int cmp_status;
|
||||
uLong src_len = (uLong)strlen(s_pStr);
|
||||
uLong cmp_len = compressBound(src_len);
|
||||
uLong uncomp_len = src_len;
|
||||
uint8 *pCmp, *pUncomp;
|
||||
uint total_succeeded = 0;
|
||||
(void)argc, (void)argv;
|
||||
|
||||
printf("miniz.c version: %s\n", MZ_VERSION);
|
||||
|
||||
do
|
||||
{
|
||||
// Allocate buffers to hold compressed and uncompressed data.
|
||||
pCmp = (mz_uint8 *)malloc((size_t)cmp_len);
|
||||
pUncomp = (mz_uint8 *)malloc((size_t)src_len);
|
||||
if ((!pCmp) || (!pUncomp))
|
||||
{
|
||||
printf("Out of memory!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Compress the string.
|
||||
cmp_status = compress(pCmp, &cmp_len, (const unsigned char *)s_pStr, src_len);
|
||||
if (cmp_status != Z_OK)
|
||||
{
|
||||
printf("compress() failed!\n");
|
||||
free(pCmp);
|
||||
free(pUncomp);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
printf("Compressed from %u to %u bytes\n", (mz_uint32)src_len, (mz_uint32)cmp_len);
|
||||
|
||||
if (step)
|
||||
{
|
||||
// Purposely corrupt the compressed data if fuzzy testing (this is a very crude fuzzy test).
|
||||
uint n = 1 + (rand() % 3);
|
||||
while (n--)
|
||||
{
|
||||
uint i = rand() % cmp_len;
|
||||
pCmp[i] ^= (rand() & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
// Decompress.
|
||||
cmp_status = uncompress(pUncomp, &uncomp_len, pCmp, cmp_len);
|
||||
total_succeeded += (cmp_status == Z_OK);
|
||||
|
||||
if (step)
|
||||
{
|
||||
printf("Simple fuzzy test: step %u total_succeeded: %u\n", step, total_succeeded);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (cmp_status != Z_OK)
|
||||
{
|
||||
printf("uncompress failed!\n");
|
||||
free(pCmp);
|
||||
free(pUncomp);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
printf("Decompressed from %u to %u bytes\n", (mz_uint32)cmp_len, (mz_uint32)uncomp_len);
|
||||
|
||||
// Ensure uncompress() returned the expected data.
|
||||
if ((uncomp_len != src_len) || (memcmp(pUncomp, s_pStr, (size_t)src_len)))
|
||||
{
|
||||
printf("Decompression failed!\n");
|
||||
free(pCmp);
|
||||
free(pUncomp);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
free(pCmp);
|
||||
free(pUncomp);
|
||||
|
||||
step++;
|
||||
|
||||
// Keep on fuzzy testing if there's a non-empty command line.
|
||||
} while (argc >= 2);
|
||||
|
||||
printf("Success.\n");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -1,164 +0,0 @@
|
||||
// example2.c - Simple demonstration of miniz.c's ZIP archive API's.
|
||||
// Note this test deletes the test archive file "__mz_example2_test__.zip" in the current directory, then creates a new one with test data.
|
||||
// Public domain, May 15 2011, Rich Geldreich, richgel99@gmail.com. See "unlicense" statement at the end of tinfl.c.
|
||||
|
||||
#if defined(__GNUC__)
|
||||
// Ensure we get the 64-bit variants of the CRT's file I/O calls
|
||||
#ifndef _FILE_OFFSET_BITS
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
#endif
|
||||
#ifndef _LARGEFILE64_SOURCE
|
||||
#define _LARGEFILE64_SOURCE 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include "miniz.h"
|
||||
|
||||
typedef unsigned char uint8;
|
||||
typedef unsigned short uint16;
|
||||
typedef unsigned int uint;
|
||||
|
||||
// The string to compress.
|
||||
static const char *s_pTest_str =
|
||||
"MISSION CONTROL I wouldn't worry too much about the computer. First of all, there is still a chance that he is right, despite your tests, and" \
|
||||
"if it should happen again, we suggest eliminating this possibility by allowing the unit to remain in place and seeing whether or not it" \
|
||||
"actually fails. If the computer should turn out to be wrong, the situation is still not alarming. The type of obsessional error he may be" \
|
||||
"guilty of is not unknown among the latest generation of HAL 9000 computers. It has almost always revolved around a single detail, such as" \
|
||||
"the one you have described, and it has never interfered with the integrity or reliability of the computer's performance in other areas." \
|
||||
"No one is certain of the cause of this kind of malfunctioning. It may be over-programming, but it could also be any number of reasons. In any" \
|
||||
"event, it is somewhat analogous to human neurotic behavior. Does this answer your query? Zero-five-three-Zero, MC, transmission concluded.";
|
||||
|
||||
static const char *s_pComment = "This is a comment";
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int i, sort_iter;
|
||||
mz_bool status;
|
||||
size_t uncomp_size;
|
||||
mz_zip_archive zip_archive;
|
||||
void *p;
|
||||
const int N = 50;
|
||||
char data[2048];
|
||||
char archive_filename[64];
|
||||
static const char *s_Test_archive_filename = "__mz_example2_test__.zip";
|
||||
|
||||
assert((strlen(s_pTest_str) + 64) < sizeof(data));
|
||||
|
||||
printf("miniz.c version: %s\n", MZ_VERSION);
|
||||
|
||||
(void)argc, (void)argv;
|
||||
|
||||
// Delete the test archive, so it doesn't keep growing as we run this test
|
||||
remove(s_Test_archive_filename);
|
||||
|
||||
// Append a bunch of text files to the test archive
|
||||
for (i = (N - 1); i >= 0; --i)
|
||||
{
|
||||
sprintf(archive_filename, "%u.txt", i);
|
||||
sprintf(data, "%u %s %u", (N - 1) - i, s_pTest_str, i);
|
||||
|
||||
// Add a new file to the archive. Note this is an IN-PLACE operation, so if it fails your archive is probably hosed (its central directory may not be complete) but it should be recoverable using zip -F or -FF. So use caution with this guy.
|
||||
// A more robust way to add a file to an archive would be to read it into memory, perform the operation, then write a new archive out to a temp file and then delete/rename the files.
|
||||
// Or, write a new archive to disk to a temp file, then delete/rename the files. For this test this API is fine.
|
||||
status = mz_zip_add_mem_to_archive_file_in_place(s_Test_archive_filename, archive_filename, data, strlen(data) + 1, s_pComment, (uint16)strlen(s_pComment), MZ_BEST_COMPRESSION);
|
||||
if (!status)
|
||||
{
|
||||
printf("mz_zip_add_mem_to_archive_file_in_place failed!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
// Add a directory entry for testing
|
||||
status = mz_zip_add_mem_to_archive_file_in_place(s_Test_archive_filename, "directory/", NULL, 0, "no comment", (uint16)strlen("no comment"), MZ_BEST_COMPRESSION);
|
||||
if (!status)
|
||||
{
|
||||
printf("mz_zip_add_mem_to_archive_file_in_place failed!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Now try to open the archive.
|
||||
memset(&zip_archive, 0, sizeof(zip_archive));
|
||||
|
||||
status = mz_zip_reader_init_file(&zip_archive, s_Test_archive_filename, 0);
|
||||
if (!status)
|
||||
{
|
||||
printf("mz_zip_reader_init_file() failed!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Get and print information about each file in the archive.
|
||||
for (i = 0; i < (int)mz_zip_reader_get_num_files(&zip_archive); i++)
|
||||
{
|
||||
mz_zip_archive_file_stat file_stat;
|
||||
if (!mz_zip_reader_file_stat(&zip_archive, i, &file_stat))
|
||||
{
|
||||
printf("mz_zip_reader_file_stat() failed!\n");
|
||||
mz_zip_reader_end(&zip_archive);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
printf("Filename: \"%s\", Comment: \"%s\", Uncompressed size: %u, Compressed size: %u, Is Dir: %u\n", file_stat.m_filename, file_stat.m_comment, (uint)file_stat.m_uncomp_size, (uint)file_stat.m_comp_size, mz_zip_reader_is_file_a_directory(&zip_archive, i));
|
||||
|
||||
if (!strcmp(file_stat.m_filename, "directory/"))
|
||||
{
|
||||
if (!mz_zip_reader_is_file_a_directory(&zip_archive, i))
|
||||
{
|
||||
printf("mz_zip_reader_is_file_a_directory() didn't return the expected results!\n");
|
||||
mz_zip_reader_end(&zip_archive);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Close the archive, freeing any resources it was using
|
||||
mz_zip_reader_end(&zip_archive);
|
||||
|
||||
// Now verify the compressed data
|
||||
for (sort_iter = 0; sort_iter < 2; sort_iter++)
|
||||
{
|
||||
memset(&zip_archive, 0, sizeof(zip_archive));
|
||||
status = mz_zip_reader_init_file(&zip_archive, s_Test_archive_filename, sort_iter ? MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY : 0);
|
||||
if (!status)
|
||||
{
|
||||
printf("mz_zip_reader_init_file() failed!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
for (i = 0; i < N; i++)
|
||||
{
|
||||
sprintf(archive_filename, "%u.txt", i);
|
||||
sprintf(data, "%u %s %u", (N - 1) - i, s_pTest_str, i);
|
||||
|
||||
// Try to extract all the files to the heap.
|
||||
p = mz_zip_reader_extract_file_to_heap(&zip_archive, archive_filename, &uncomp_size, 0);
|
||||
if (!p)
|
||||
{
|
||||
printf("mz_zip_reader_extract_file_to_heap() failed!\n");
|
||||
mz_zip_reader_end(&zip_archive);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Make sure the extraction really succeeded.
|
||||
if ((uncomp_size != (strlen(data) + 1)) || (memcmp(p, data, strlen(data))))
|
||||
{
|
||||
printf("mz_zip_reader_extract_file_to_heap() failed to extract the proper data\n");
|
||||
mz_free(p);
|
||||
mz_zip_reader_end(&zip_archive);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
printf("Successfully extracted file \"%s\", size %u\n", archive_filename, (uint)uncomp_size);
|
||||
printf("File data: \"%s\"\n", (const char *)p);
|
||||
|
||||
// We're done.
|
||||
mz_free(p);
|
||||
}
|
||||
|
||||
// Close the archive, freeing any resources it was using
|
||||
mz_zip_reader_end(&zip_archive);
|
||||
}
|
||||
|
||||
printf("Success.\n");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -1,269 +0,0 @@
|
||||
// example3.c - Demonstrates how to use miniz.c's deflate() and inflate() functions for simple file compression.
|
||||
// Public domain, May 15 2011, Rich Geldreich, richgel99@gmail.com. See "unlicense" statement at the end of tinfl.c.
|
||||
// For simplicity, this example is limited to files smaller than 4GB, but this is not a limitation of miniz.c.
|
||||
#include <stdio.h>
|
||||
#include <limits.h>
|
||||
#include "miniz.h"
|
||||
|
||||
typedef unsigned char uint8;
|
||||
typedef unsigned short uint16;
|
||||
typedef unsigned int uint;
|
||||
|
||||
#define my_max(a,b) (((a) > (b)) ? (a) : (b))
|
||||
#define my_min(a,b) (((a) < (b)) ? (a) : (b))
|
||||
|
||||
#define BUF_SIZE (1024 * 1024)
|
||||
static uint8 s_inbuf[BUF_SIZE];
|
||||
static uint8 s_outbuf[BUF_SIZE];
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
const char *pMode;
|
||||
FILE *pInfile, *pOutfile;
|
||||
uint infile_size;
|
||||
int level = Z_BEST_COMPRESSION;
|
||||
z_stream stream;
|
||||
int p = 1;
|
||||
const char *pSrc_filename;
|
||||
const char *pDst_filename;
|
||||
long file_loc;
|
||||
|
||||
printf("miniz.c version: %s\n", MZ_VERSION);
|
||||
|
||||
if (argc < 4)
|
||||
{
|
||||
printf("Usage: example3 [options] [mode:c or d] infile outfile\n");
|
||||
printf("\nModes:\n");
|
||||
printf("c - Compresses file infile to a zlib stream in file outfile\n");
|
||||
printf("d - Decompress zlib stream in file infile to file outfile\n");
|
||||
printf("\nOptions:\n");
|
||||
printf("-l[0-10] - Compression level, higher values are slower.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
while ((p < argc) && (argv[p][0] == '-'))
|
||||
{
|
||||
switch (argv[p][1])
|
||||
{
|
||||
case 'l':
|
||||
{
|
||||
level = atoi(&argv[1][2]);
|
||||
if ((level < 0) || (level > 10))
|
||||
{
|
||||
printf("Invalid level!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
printf("Invalid option: %s\n", argv[p]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
p++;
|
||||
}
|
||||
|
||||
if ((argc - p) < 3)
|
||||
{
|
||||
printf("Must specify mode, input filename, and output filename after options!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
else if ((argc - p) > 3)
|
||||
{
|
||||
printf("Too many filenames!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
pMode = argv[p++];
|
||||
if (!strchr("cCdD", pMode[0]))
|
||||
{
|
||||
printf("Invalid mode!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
pSrc_filename = argv[p++];
|
||||
pDst_filename = argv[p++];
|
||||
|
||||
printf("Mode: %c, Level: %u\nInput File: \"%s\"\nOutput File: \"%s\"\n", pMode[0], level, pSrc_filename, pDst_filename);
|
||||
|
||||
// Open input file.
|
||||
pInfile = fopen(pSrc_filename, "rb");
|
||||
if (!pInfile)
|
||||
{
|
||||
printf("Failed opening input file!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Determine input file's size.
|
||||
fseek(pInfile, 0, SEEK_END);
|
||||
file_loc = ftell(pInfile);
|
||||
fseek(pInfile, 0, SEEK_SET);
|
||||
|
||||
if ((file_loc < 0) || ((mz_uint64)file_loc > INT_MAX))
|
||||
{
|
||||
// This is not a limitation of miniz or tinfl, but this example.
|
||||
printf("File is too large to be processed by this example.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
infile_size = (uint)file_loc;
|
||||
|
||||
// Open output file.
|
||||
pOutfile = fopen(pDst_filename, "wb");
|
||||
if (!pOutfile)
|
||||
{
|
||||
printf("Failed opening output file!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
printf("Input file size: %u\n", infile_size);
|
||||
|
||||
// Init the z_stream
|
||||
memset(&stream, 0, sizeof(stream));
|
||||
stream.next_in = s_inbuf;
|
||||
stream.avail_in = 0;
|
||||
stream.next_out = s_outbuf;
|
||||
stream.avail_out = BUF_SIZE;
|
||||
|
||||
if ((pMode[0] == 'c') || (pMode[0] == 'C'))
|
||||
{
|
||||
// Compression.
|
||||
uint infile_remaining = infile_size;
|
||||
|
||||
if (deflateInit(&stream, level) != Z_OK)
|
||||
{
|
||||
printf("deflateInit() failed!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
for ( ; ; )
|
||||
{
|
||||
int status;
|
||||
if (!stream.avail_in)
|
||||
{
|
||||
// Input buffer is empty, so read more bytes from input file.
|
||||
uint n = my_min(BUF_SIZE, infile_remaining);
|
||||
|
||||
if (fread(s_inbuf, 1, n, pInfile) != n)
|
||||
{
|
||||
printf("Failed reading from input file!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
stream.next_in = s_inbuf;
|
||||
stream.avail_in = n;
|
||||
|
||||
infile_remaining -= n;
|
||||
//printf("Input bytes remaining: %u\n", infile_remaining);
|
||||
}
|
||||
|
||||
status = deflate(&stream, infile_remaining ? Z_NO_FLUSH : Z_FINISH);
|
||||
|
||||
if ((status == Z_STREAM_END) || (!stream.avail_out))
|
||||
{
|
||||
// Output buffer is full, or compression is done, so write buffer to output file.
|
||||
uint n = BUF_SIZE - stream.avail_out;
|
||||
if (fwrite(s_outbuf, 1, n, pOutfile) != n)
|
||||
{
|
||||
printf("Failed writing to output file!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
stream.next_out = s_outbuf;
|
||||
stream.avail_out = BUF_SIZE;
|
||||
}
|
||||
|
||||
if (status == Z_STREAM_END)
|
||||
break;
|
||||
else if (status != Z_OK)
|
||||
{
|
||||
printf("deflate() failed with status %i!\n", status);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
if (deflateEnd(&stream) != Z_OK)
|
||||
{
|
||||
printf("deflateEnd() failed!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
else if ((pMode[0] == 'd') || (pMode[0] == 'D'))
|
||||
{
|
||||
// Decompression.
|
||||
uint infile_remaining = infile_size;
|
||||
|
||||
if (inflateInit(&stream))
|
||||
{
|
||||
printf("inflateInit() failed!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
for ( ; ; )
|
||||
{
|
||||
int status;
|
||||
if (!stream.avail_in)
|
||||
{
|
||||
// Input buffer is empty, so read more bytes from input file.
|
||||
uint n = my_min(BUF_SIZE, infile_remaining);
|
||||
|
||||
if (fread(s_inbuf, 1, n, pInfile) != n)
|
||||
{
|
||||
printf("Failed reading from input file!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
stream.next_in = s_inbuf;
|
||||
stream.avail_in = n;
|
||||
|
||||
infile_remaining -= n;
|
||||
}
|
||||
|
||||
status = inflate(&stream, Z_SYNC_FLUSH);
|
||||
|
||||
if ((status == Z_STREAM_END) || (!stream.avail_out))
|
||||
{
|
||||
// Output buffer is full, or decompression is done, so write buffer to output file.
|
||||
uint n = BUF_SIZE - stream.avail_out;
|
||||
if (fwrite(s_outbuf, 1, n, pOutfile) != n)
|
||||
{
|
||||
printf("Failed writing to output file!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
stream.next_out = s_outbuf;
|
||||
stream.avail_out = BUF_SIZE;
|
||||
}
|
||||
|
||||
if (status == Z_STREAM_END)
|
||||
break;
|
||||
else if (status != Z_OK)
|
||||
{
|
||||
printf("inflate() failed with status %i!\n", status);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
if (inflateEnd(&stream) != Z_OK)
|
||||
{
|
||||
printf("inflateEnd() failed!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Invalid mode!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
fclose(pInfile);
|
||||
if (EOF == fclose(pOutfile))
|
||||
{
|
||||
printf("Failed writing to output file!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
printf("Total input bytes: %u\n", (mz_uint32)stream.total_in);
|
||||
printf("Total output bytes: %u\n", (mz_uint32)stream.total_out);
|
||||
printf("Success.\n");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
// example4.c - Uses tinfl.c to decompress a zlib stream in memory to an output file
|
||||
// Public domain, May 15 2011, Rich Geldreich, richgel99@gmail.com. See "unlicense" statement at the end of tinfl.c.
|
||||
#include "miniz.h"
|
||||
#include <stdio.h>
|
||||
#include <limits.h>
|
||||
|
||||
typedef unsigned char uint8;
|
||||
typedef unsigned short uint16;
|
||||
typedef unsigned int uint;
|
||||
|
||||
#define my_max(a,b) (((a) > (b)) ? (a) : (b))
|
||||
#define my_min(a,b) (((a) < (b)) ? (a) : (b))
|
||||
|
||||
static int tinfl_put_buf_func(const void* pBuf, int len, void *pUser)
|
||||
{
|
||||
return len == (int)fwrite(pBuf, 1, len, (FILE*)pUser);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int status;
|
||||
FILE *pInfile, *pOutfile;
|
||||
uint infile_size, outfile_size;
|
||||
size_t in_buf_size;
|
||||
uint8 *pCmp_data;
|
||||
long file_loc;
|
||||
|
||||
if (argc != 3)
|
||||
{
|
||||
printf("Usage: example4 infile outfile\n");
|
||||
printf("Decompresses zlib stream in file infile to file outfile.\n");
|
||||
printf("Input file must be able to fit entirely in memory.\n");
|
||||
printf("example3 can be used to create compressed zlib streams.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Open input file.
|
||||
pInfile = fopen(argv[1], "rb");
|
||||
if (!pInfile)
|
||||
{
|
||||
printf("Failed opening input file!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Determine input file's size.
|
||||
fseek(pInfile, 0, SEEK_END);
|
||||
file_loc = ftell(pInfile);
|
||||
fseek(pInfile, 0, SEEK_SET);
|
||||
|
||||
if ((file_loc < 0) || ((mz_uint64)file_loc > INT_MAX))
|
||||
{
|
||||
// This is not a limitation of miniz or tinfl, but this example.
|
||||
printf("File is too large to be processed by this example.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
infile_size = (uint)file_loc;
|
||||
|
||||
pCmp_data = (uint8 *)malloc(infile_size);
|
||||
if (!pCmp_data)
|
||||
{
|
||||
printf("Out of memory!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (fread(pCmp_data, 1, infile_size, pInfile) != infile_size)
|
||||
{
|
||||
printf("Failed reading input file!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Open output file.
|
||||
pOutfile = fopen(argv[2], "wb");
|
||||
if (!pOutfile)
|
||||
{
|
||||
printf("Failed opening output file!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
printf("Input file size: %u\n", infile_size);
|
||||
|
||||
in_buf_size = infile_size;
|
||||
status = tinfl_decompress_mem_to_callback(pCmp_data, &in_buf_size, tinfl_put_buf_func, pOutfile, TINFL_FLAG_PARSE_ZLIB_HEADER);
|
||||
if (!status)
|
||||
{
|
||||
printf("tinfl_decompress_mem_to_callback() failed with status %i!\n", status);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
outfile_size = ftell(pOutfile);
|
||||
|
||||
fclose(pInfile);
|
||||
if (EOF == fclose(pOutfile))
|
||||
{
|
||||
printf("Failed writing to output file!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
printf("Total input bytes: %u\n", (uint)in_buf_size);
|
||||
printf("Total output bytes: %u\n", outfile_size);
|
||||
printf("Success.\n");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -1,327 +0,0 @@
|
||||
// example5.c - Demonstrates how to use miniz.c's low-level tdefl_compress() and tinfl_inflate() API's for simple file to file compression/decompression.
|
||||
// The low-level API's are the fastest, make no use of dynamic memory allocation, and are the most flexible functions exposed by miniz.c.
|
||||
// Public domain, April 11 2012, Rich Geldreich, richgel99@gmail.com. See "unlicense" statement at the end of tinfl.c.
|
||||
// For simplicity, this example is limited to files smaller than 4GB, but this is not a limitation of miniz.c.
|
||||
|
||||
// Purposely disable a whole bunch of stuff this low-level example doesn't use.
|
||||
#define MINIZ_NO_STDIO
|
||||
#define MINIZ_NO_ARCHIVE_APIS
|
||||
#define MINIZ_NO_TIME
|
||||
#define MINIZ_NO_ZLIB_APIS
|
||||
#define MINIZ_NO_MALLOC
|
||||
#include "miniz.h"
|
||||
|
||||
// Now include stdio.h because this test uses fopen(), etc. (but we still don't want miniz.c's stdio stuff, for testing).
|
||||
#include <stdio.h>
|
||||
#include <limits.h>
|
||||
|
||||
typedef unsigned char uint8;
|
||||
typedef unsigned short uint16;
|
||||
typedef unsigned int uint;
|
||||
|
||||
#define my_max(a,b) (((a) > (b)) ? (a) : (b))
|
||||
#define my_min(a,b) (((a) < (b)) ? (a) : (b))
|
||||
|
||||
// IN_BUF_SIZE is the size of the file read buffer.
|
||||
// IN_BUF_SIZE must be >= 1
|
||||
#define IN_BUF_SIZE (1024*512)
|
||||
static uint8 s_inbuf[IN_BUF_SIZE];
|
||||
|
||||
// COMP_OUT_BUF_SIZE is the size of the output buffer used during compression.
|
||||
// COMP_OUT_BUF_SIZE must be >= 1 and <= OUT_BUF_SIZE
|
||||
#define COMP_OUT_BUF_SIZE (1024*512)
|
||||
|
||||
// OUT_BUF_SIZE is the size of the output buffer used during decompression.
|
||||
// OUT_BUF_SIZE must be a power of 2 >= TINFL_LZ_DICT_SIZE (because the low-level decompressor not only writes, but reads from the output buffer as it decompresses)
|
||||
//#define OUT_BUF_SIZE (TINFL_LZ_DICT_SIZE)
|
||||
#define OUT_BUF_SIZE (1024*512)
|
||||
static uint8 s_outbuf[OUT_BUF_SIZE];
|
||||
|
||||
// tdefl_compressor contains all the state needed by the low-level compressor so it's a pretty big struct (~300k).
|
||||
// This example makes it a global vs. putting it on the stack, of course in real-world usage you'll probably malloc() or new it.
|
||||
tdefl_compressor g_deflator;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
const char *pMode;
|
||||
FILE *pInfile, *pOutfile;
|
||||
uint infile_size;
|
||||
int level = 9;
|
||||
int p = 1;
|
||||
const char *pSrc_filename;
|
||||
const char *pDst_filename;
|
||||
const void *next_in = s_inbuf;
|
||||
size_t avail_in = 0;
|
||||
void *next_out = s_outbuf;
|
||||
size_t avail_out = OUT_BUF_SIZE;
|
||||
size_t total_in = 0, total_out = 0;
|
||||
long file_loc;
|
||||
|
||||
assert(COMP_OUT_BUF_SIZE <= OUT_BUF_SIZE);
|
||||
|
||||
printf("miniz.c example5 (demonstrates tinfl/tdefl)\n");
|
||||
|
||||
if (argc < 4)
|
||||
{
|
||||
printf("File to file compression/decompression using the low-level tinfl/tdefl API's.\n");
|
||||
printf("Usage: example5 [options] [mode:c or d] infile outfile\n");
|
||||
printf("\nModes:\n");
|
||||
printf("c - Compresses file infile to a zlib stream in file outfile\n");
|
||||
printf("d - Decompress zlib stream in file infile to file outfile\n");
|
||||
printf("\nOptions:\n");
|
||||
printf("-l[0-10] - Compression level, higher values are slower, 0 is none.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
while ((p < argc) && (argv[p][0] == '-'))
|
||||
{
|
||||
switch (argv[p][1])
|
||||
{
|
||||
case 'l':
|
||||
{
|
||||
level = atoi(&argv[1][2]);
|
||||
if ((level < 0) || (level > 10))
|
||||
{
|
||||
printf("Invalid level!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
printf("Invalid option: %s\n", argv[p]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
p++;
|
||||
}
|
||||
|
||||
if ((argc - p) < 3)
|
||||
{
|
||||
printf("Must specify mode, input filename, and output filename after options!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
else if ((argc - p) > 3)
|
||||
{
|
||||
printf("Too many filenames!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
pMode = argv[p++];
|
||||
if (!strchr("cCdD", pMode[0]))
|
||||
{
|
||||
printf("Invalid mode!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
pSrc_filename = argv[p++];
|
||||
pDst_filename = argv[p++];
|
||||
|
||||
printf("Mode: %c, Level: %u\nInput File: \"%s\"\nOutput File: \"%s\"\n", pMode[0], level, pSrc_filename, pDst_filename);
|
||||
|
||||
// Open input file.
|
||||
pInfile = fopen(pSrc_filename, "rb");
|
||||
if (!pInfile)
|
||||
{
|
||||
printf("Failed opening input file!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Determine input file's size.
|
||||
fseek(pInfile, 0, SEEK_END);
|
||||
file_loc = ftell(pInfile);
|
||||
fseek(pInfile, 0, SEEK_SET);
|
||||
|
||||
if ((file_loc < 0) || ((mz_uint64)file_loc > INT_MAX))
|
||||
{
|
||||
// This is not a limitation of miniz or tinfl, but this example.
|
||||
printf("File is too large to be processed by this example.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
infile_size = (uint)file_loc;
|
||||
|
||||
// Open output file.
|
||||
pOutfile = fopen(pDst_filename, "wb");
|
||||
if (!pOutfile)
|
||||
{
|
||||
printf("Failed opening output file!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
printf("Input file size: %u\n", infile_size);
|
||||
|
||||
if ((pMode[0] == 'c') || (pMode[0] == 'C'))
|
||||
{
|
||||
// The number of dictionary probes to use at each compression level (0-10). 0=implies fastest/minimal possible probing.
|
||||
static const mz_uint s_tdefl_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 };
|
||||
|
||||
tdefl_status status;
|
||||
uint infile_remaining = infile_size;
|
||||
|
||||
// create tdefl() compatible flags (we have to compose the low-level flags ourselves, or use tdefl_create_comp_flags_from_zip_params() but that means MINIZ_NO_ZLIB_APIS can't be defined).
|
||||
mz_uint comp_flags = TDEFL_WRITE_ZLIB_HEADER | s_tdefl_num_probes[MZ_MIN(10, level)] | ((level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0);
|
||||
if (!level)
|
||||
comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS;
|
||||
|
||||
// Initialize the low-level compressor.
|
||||
status = tdefl_init(&g_deflator, NULL, NULL, comp_flags);
|
||||
if (status != TDEFL_STATUS_OKAY)
|
||||
{
|
||||
printf("tdefl_init() failed!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
avail_out = COMP_OUT_BUF_SIZE;
|
||||
|
||||
// Compression.
|
||||
for ( ; ; )
|
||||
{
|
||||
size_t in_bytes, out_bytes;
|
||||
|
||||
if (!avail_in)
|
||||
{
|
||||
// Input buffer is empty, so read more bytes from input file.
|
||||
uint n = my_min(IN_BUF_SIZE, infile_remaining);
|
||||
|
||||
if (fread(s_inbuf, 1, n, pInfile) != n)
|
||||
{
|
||||
printf("Failed reading from input file!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
next_in = s_inbuf;
|
||||
avail_in = n;
|
||||
|
||||
infile_remaining -= n;
|
||||
//printf("Input bytes remaining: %u\n", infile_remaining);
|
||||
}
|
||||
|
||||
in_bytes = avail_in;
|
||||
out_bytes = avail_out;
|
||||
// Compress as much of the input as possible (or all of it) to the output buffer.
|
||||
status = tdefl_compress(&g_deflator, next_in, &in_bytes, next_out, &out_bytes, infile_remaining ? TDEFL_NO_FLUSH : TDEFL_FINISH);
|
||||
|
||||
next_in = (const char *)next_in + in_bytes;
|
||||
avail_in -= in_bytes;
|
||||
total_in += in_bytes;
|
||||
|
||||
next_out = (char *)next_out + out_bytes;
|
||||
avail_out -= out_bytes;
|
||||
total_out += out_bytes;
|
||||
|
||||
if ((status != TDEFL_STATUS_OKAY) || (!avail_out))
|
||||
{
|
||||
// Output buffer is full, or compression is done or failed, so write buffer to output file.
|
||||
uint n = COMP_OUT_BUF_SIZE - (uint)avail_out;
|
||||
if (fwrite(s_outbuf, 1, n, pOutfile) != n)
|
||||
{
|
||||
printf("Failed writing to output file!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
next_out = s_outbuf;
|
||||
avail_out = COMP_OUT_BUF_SIZE;
|
||||
}
|
||||
|
||||
if (status == TDEFL_STATUS_DONE)
|
||||
{
|
||||
// Compression completed successfully.
|
||||
break;
|
||||
}
|
||||
else if (status != TDEFL_STATUS_OKAY)
|
||||
{
|
||||
// Compression somehow failed.
|
||||
printf("tdefl_compress() failed with status %i!\n", status);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ((pMode[0] == 'd') || (pMode[0] == 'D'))
|
||||
{
|
||||
// Decompression.
|
||||
uint infile_remaining = infile_size;
|
||||
|
||||
tinfl_decompressor inflator;
|
||||
tinfl_init(&inflator);
|
||||
|
||||
for ( ; ; )
|
||||
{
|
||||
size_t in_bytes, out_bytes;
|
||||
tinfl_status status;
|
||||
if (!avail_in)
|
||||
{
|
||||
// Input buffer is empty, so read more bytes from input file.
|
||||
uint n = my_min(IN_BUF_SIZE, infile_remaining);
|
||||
|
||||
if (fread(s_inbuf, 1, n, pInfile) != n)
|
||||
{
|
||||
printf("Failed reading from input file!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
next_in = s_inbuf;
|
||||
avail_in = n;
|
||||
|
||||
infile_remaining -= n;
|
||||
}
|
||||
|
||||
in_bytes = avail_in;
|
||||
out_bytes = avail_out;
|
||||
status = tinfl_decompress(&inflator, (const mz_uint8 *)next_in, &in_bytes, s_outbuf, (mz_uint8 *)next_out, &out_bytes, (infile_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0) | TINFL_FLAG_PARSE_ZLIB_HEADER);
|
||||
|
||||
avail_in -= in_bytes;
|
||||
next_in = (const mz_uint8 *)next_in + in_bytes;
|
||||
total_in += in_bytes;
|
||||
|
||||
avail_out -= out_bytes;
|
||||
next_out = (mz_uint8 *)next_out + out_bytes;
|
||||
total_out += out_bytes;
|
||||
|
||||
if ((status <= TINFL_STATUS_DONE) || (!avail_out))
|
||||
{
|
||||
// Output buffer is full, or decompression is done, so write buffer to output file.
|
||||
uint n = OUT_BUF_SIZE - (uint)avail_out;
|
||||
if (fwrite(s_outbuf, 1, n, pOutfile) != n)
|
||||
{
|
||||
printf("Failed writing to output file!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
next_out = s_outbuf;
|
||||
avail_out = OUT_BUF_SIZE;
|
||||
}
|
||||
|
||||
// If status is <= TINFL_STATUS_DONE then either decompression is done or something went wrong.
|
||||
if (status <= TINFL_STATUS_DONE)
|
||||
{
|
||||
if (status == TINFL_STATUS_DONE)
|
||||
{
|
||||
// Decompression completed successfully.
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Decompression failed.
|
||||
printf("tinfl_decompress() failed with status %i!\n", status);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Invalid mode!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
fclose(pInfile);
|
||||
if (EOF == fclose(pOutfile))
|
||||
{
|
||||
printf("Failed writing to output file!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
printf("Total input bytes: %u\n", (mz_uint32)total_in);
|
||||
printf("Total output bytes: %u\n", (mz_uint32)total_out);
|
||||
printf("Success.\n");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -1,166 +0,0 @@
|
||||
// example6.c - Demonstrates how to miniz's PNG writer func
|
||||
// Public domain, April 11 2012, Rich Geldreich, richgel99@gmail.com. See "unlicense" statement at the end of tinfl.c.
|
||||
// Mandlebrot set code from http://rosettacode.org/wiki/Mandelbrot_set#C
|
||||
// Must link this example against libm on Linux.
|
||||
|
||||
// Purposely disable a whole bunch of stuff this low-level example doesn't use.
|
||||
#define MINIZ_NO_STDIO
|
||||
#define MINIZ_NO_TIME
|
||||
#define MINIZ_NO_ZLIB_APIS
|
||||
#include "miniz.h"
|
||||
|
||||
// Now include stdio.h because this test uses fopen(), etc. (but we still don't want miniz.c's stdio stuff, for testing).
|
||||
#include <stdio.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
|
||||
typedef unsigned char uint8;
|
||||
typedef unsigned short uint16;
|
||||
typedef unsigned int uint;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8 r, g, b;
|
||||
} rgb_t;
|
||||
|
||||
static void hsv_to_rgb(int hue, int min, int max, rgb_t *p)
|
||||
{
|
||||
const int invert = 0;
|
||||
const int saturation = 1;
|
||||
const int color_rotate = 0;
|
||||
|
||||
if (min == max) max = min + 1;
|
||||
if (invert) hue = max - (hue - min);
|
||||
if (!saturation) {
|
||||
p->r = p->g = p->b = 255 * (max - hue) / (max - min);
|
||||
return;
|
||||
} else {
|
||||
const double h_dbl = fmod(color_rotate + 1e-4 + 4.0 * (hue - min) / (max - min), 6);
|
||||
const double c_dbl = 255 * saturation;
|
||||
const double X_dbl = c_dbl * (1 - fabs(fmod(h_dbl, 2) - 1));
|
||||
const int h = (int)h_dbl;
|
||||
const int c = (int)c_dbl;
|
||||
const int X = (int)X_dbl;
|
||||
|
||||
p->r = p->g = p->b = 0;
|
||||
|
||||
switch(h) {
|
||||
case 0: p->r = c; p->g = X; return;
|
||||
case 1: p->r = X; p->g = c; return;
|
||||
case 2: p->g = c; p->b = X; return;
|
||||
case 3: p->g = X; p->b = c; return;
|
||||
case 4: p->r = X; p->b = c; return;
|
||||
default:p->r = c; p->b = X;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// Image resolution
|
||||
const int iXmax = 4096;
|
||||
const int iYmax = 4096;
|
||||
|
||||
// Output filename
|
||||
static const char *pFilename = "mandelbrot.png";
|
||||
|
||||
int iX, iY;
|
||||
const double CxMin = -2.5;
|
||||
const double CxMax = 1.5;
|
||||
const double CyMin = -2.0;
|
||||
const double CyMax = 2.0;
|
||||
|
||||
double PixelWidth = (CxMax - CxMin) / iXmax;
|
||||
double PixelHeight = (CyMax - CyMin) / iYmax;
|
||||
|
||||
// Z=Zx+Zy*i ; Z0 = 0
|
||||
double Zx, Zy;
|
||||
double Zx2, Zy2; // Zx2=Zx*Zx; Zy2=Zy*Zy
|
||||
|
||||
int Iteration;
|
||||
const int IterationMax = 200;
|
||||
|
||||
// bail-out value , radius of circle
|
||||
const double EscapeRadius = 2;
|
||||
double ER2=EscapeRadius * EscapeRadius;
|
||||
|
||||
uint8 *pImage = (uint8 *)malloc(iXmax * 3 * iYmax);
|
||||
|
||||
// world ( double) coordinate = parameter plane
|
||||
double Cx,Cy;
|
||||
|
||||
int MinIter = 9999, MaxIter = 0;
|
||||
|
||||
(void)argc, (void)argv;
|
||||
|
||||
for(iY = 0; iY < iYmax; iY++)
|
||||
{
|
||||
Cy = CyMin + iY * PixelHeight;
|
||||
if (fabs(Cy) < PixelHeight/2)
|
||||
Cy = 0.0; // Main antenna
|
||||
|
||||
for(iX = 0; iX < iXmax; iX++)
|
||||
{
|
||||
uint8 *color = pImage + (iX * 3) + (iY * iXmax * 3);
|
||||
|
||||
Cx = CxMin + iX * PixelWidth;
|
||||
|
||||
// initial value of orbit = critical point Z= 0
|
||||
Zx = 0.0;
|
||||
Zy = 0.0;
|
||||
Zx2 = Zx * Zx;
|
||||
Zy2 = Zy * Zy;
|
||||
|
||||
for (Iteration=0;Iteration<IterationMax && ((Zx2+Zy2)<ER2);Iteration++)
|
||||
{
|
||||
Zy = 2 * Zx * Zy + Cy;
|
||||
Zx =Zx2 - Zy2 + Cx;
|
||||
Zx2 = Zx * Zx;
|
||||
Zy2 = Zy * Zy;
|
||||
};
|
||||
|
||||
color[0] = (uint8)Iteration;
|
||||
color[1] = (uint8)Iteration >> 8;
|
||||
color[2] = 0;
|
||||
|
||||
if (Iteration < MinIter)
|
||||
MinIter = Iteration;
|
||||
if (Iteration > MaxIter)
|
||||
MaxIter = Iteration;
|
||||
}
|
||||
}
|
||||
|
||||
for(iY = 0; iY < iYmax; iY++)
|
||||
{
|
||||
for(iX = 0; iX < iXmax; iX++)
|
||||
{
|
||||
uint8 *color = (uint8 *)(pImage + (iX * 3) + (iY * iXmax * 3));
|
||||
|
||||
uint Iterations = color[0] | (color[1] << 8U);
|
||||
|
||||
hsv_to_rgb((int)Iterations, MinIter, MaxIter, (rgb_t *)color);
|
||||
}
|
||||
}
|
||||
|
||||
// Now write the PNG image.
|
||||
{
|
||||
size_t png_data_size = 0;
|
||||
void *pPNG_data = tdefl_write_image_to_png_file_in_memory_ex(pImage, iXmax, iYmax, 3, &png_data_size, 6, MZ_FALSE);
|
||||
if (!pPNG_data)
|
||||
fprintf(stderr, "tdefl_write_image_to_png_file_in_memory_ex() failed!\n");
|
||||
else
|
||||
{
|
||||
FILE *pFile = fopen(pFilename, "wb");
|
||||
fwrite(pPNG_data, 1, png_data_size, pFile);
|
||||
fclose(pFile);
|
||||
printf("Wrote %s\n", pFilename);
|
||||
}
|
||||
|
||||
// mz_free() is by default just an alias to free() internally, but if you've overridden miniz's allocation funcs you'll probably need to call mz_free().
|
||||
mz_free(pPNG_data);
|
||||
}
|
||||
|
||||
free(pImage);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
## Miniz
|
||||
|
||||
Miniz is a lossless, high performance data compression library in a single source file that implements the zlib (RFC 1950) and Deflate (RFC 1951) compressed data format specification standards. It supports the most commonly used functions exported by the zlib library, but is a completely independent implementation so zlib's licensing requirements do not apply. Miniz also contains simple to use functions for writing .PNG format image files and reading/writing/appending .ZIP format archives. Miniz's compression speed has been tuned to be comparable to zlib's, and it also has a specialized real-time compressor function designed to compare well against fastlz/minilzo.
|
||||
|
||||
## Usage
|
||||
|
||||
Releases are available at the [releases page](https://github.com/richgel999/miniz/releases) as a pair of `miniz.c`/`miniz.h` files which can be simply added to a project. To create this file pair the different source and header files are [amalgamated](https://www.sqlite.org/amalgamation.html) during build. Alternatively use as cmake or meson module (or build system of your choice).
|
||||
|
||||
## Features
|
||||
|
||||
* MIT licensed
|
||||
* A portable, single source and header file library written in plain C. Tested with GCC, clang and Visual Studio.
|
||||
* Easily tuned and trimmed down by defines
|
||||
* A drop-in replacement for zlib's most used API's (tested in several open source projects that use zlib, such as libpng and libzip).
|
||||
* Fills a single threaded performance vs. compression ratio gap between several popular real-time compressors and zlib. For example, at level 1, miniz.c compresses around 5-9% better than minilzo, but is approx. 35% slower. At levels 2-9, miniz.c is designed to compare favorably against zlib's ratio and speed. See the miniz performance comparison page for example timings.
|
||||
* Not a block based compressor: miniz.c fully supports stream based processing using a coroutine-style implementation. The zlib-style API functions can be called a single byte at a time if that's all you've got.
|
||||
* Easy to use. The low-level compressor (tdefl) and decompressor (tinfl) have simple state structs which can be saved/restored as needed with simple memcpy's. The low-level codec API's don't use the heap in any way.
|
||||
* Entire inflater (including optional zlib header parsing and Adler-32 checking) is implemented in a single function as a coroutine, which is separately available in a small (~550 line) source file: miniz_tinfl.c
|
||||
* A fairly complete (but totally optional) set of .ZIP archive manipulation and extraction API's. The archive functionality is intended to solve common problems encountered in embedded, mobile, or game development situations. (The archive API's are purposely just powerful enough to write an entire archiver given a bit of additional higher-level logic.)
|
||||
|
||||
## Building miniz - Using vcpkg
|
||||
|
||||
You can download and install miniz using the [vcpkg](https://github.com/Microsoft/vcpkg) dependency manager:
|
||||
|
||||
git clone https://github.com/Microsoft/vcpkg.git
|
||||
cd vcpkg
|
||||
./bootstrap-vcpkg.sh
|
||||
./vcpkg integrate install
|
||||
./vcpkg install miniz
|
||||
|
||||
The miniz port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository.
|
||||
|
||||
## Known Problems
|
||||
|
||||
* No support for encrypted archives. Not sure how useful this stuff is in practice.
|
||||
* Minimal documentation. The assumption is that the user is already familiar with the basic zlib API. I need to write an API wiki - for now I've tried to place key comments before each enum/API, and I've included 6 examples that demonstrate how to use the module's major features.
|
||||
|
||||
## Special Thanks
|
||||
|
||||
Thanks to Alex Evans for the PNG writer function. Also, thanks to Paul Holden and Thorsten Scheuermann for feedback and testing, Matt Pritchard for all his encouragement, and Sean Barrett's various public domain libraries for inspiration (and encouraging me to write miniz.c in C, which was much more enjoyable and less painful than I thought it would be considering I've been programming in C++ for so long).
|
||||
|
||||
Thanks to Bruce Dawson for reporting a problem with the level_and_flags archive API parameter (which is fixed in v1.12) and general feedback, and Janez Zemva for indirectly encouraging me into writing more examples.
|
||||
|
||||
## Patents
|
||||
|
||||
I was recently asked if miniz avoids patent issues. miniz purposely uses the same core algorithms as the ones used by zlib. The compressor uses vanilla hash chaining as described [here](https://datatracker.ietf.org/doc/html/rfc1951#section-4). Also see the [gzip FAQ](https://web.archive.org/web/20160308045258/http://www.gzip.org/#faq11). In my opinion, if miniz falls prey to a patent attack then zlib/gzip are likely to be at serious risk too.
|
||||
@@ -1,8 +0,0 @@
|
||||
The files in this folder are a direct extraction of the miniz release from https://github.com/richgel999/miniz/releases
|
||||
|
||||
It should be possible to include the repo directly as a submodule, how ever it causes various issues. See also
|
||||
- https://github.com/richgel999/miniz/issues/145
|
||||
- https://github.com/espressif/esptool/pull/500#issuecomment-574879468
|
||||
|
||||
For simplicity we therefore use the release files as suggested in the readme.
|
||||
Additionally we added the CMakeLists.txt and this readme.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,19 +3,20 @@
|
||||
#ifndef SERVERFILE_H
|
||||
#define SERVERFILE_H
|
||||
|
||||
#include "defines.h"
|
||||
|
||||
#include <esp_http_server.h>
|
||||
#include <string>
|
||||
|
||||
void register_server_file_uri(httpd_handle_t server, const char *base_path);
|
||||
|
||||
void unzip(std::string _in_zip_file, std::string _target_directory);
|
||||
std::string unzip_new(std::string _in_zip_file, std::string _html_tmp, std::string _html_final, std::string _target_bin, std::string _main = "/sdcard/", bool _initial_setup = false);
|
||||
|
||||
|
||||
void delete_all_in_directory(std::string _directory);
|
||||
|
||||
esp_err_t get_tflite_file_handler(httpd_req_t *req);
|
||||
esp_err_t get_data_file_handler(httpd_req_t *req);
|
||||
esp_err_t get_numbers_file_handler(httpd_req_t *req);
|
||||
|
||||
#endif //SERVERFILE_H
|
||||
void file_server_register_uri(httpd_handle_t server, const char *base_path);
|
||||
|
||||
#endif // SERVERFILE_H
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#include "defines.h"
|
||||
|
||||
#include "server_help.h"
|
||||
|
||||
#include <stdio.h>
|
||||
@@ -6,19 +8,10 @@
|
||||
#include <sys/unistd.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#include <dirent.h>
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "Helper.h"
|
||||
#include "esp_http_server.h"
|
||||
#include "../../include/defines.h"
|
||||
|
||||
static const char *TAG = "SERVER HELP";
|
||||
|
||||
@@ -26,7 +19,8 @@ char scratch[SERVER_HELPER_SCRATCH_BUFSIZE];
|
||||
|
||||
bool endsWith(std::string const &str, std::string const &suffix)
|
||||
{
|
||||
if (str.length() < suffix.length()) {
|
||||
if (str.length() < suffix.length())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return str.compare(str.length() - suffix.length(), suffix.length(), suffix) == 0;
|
||||
@@ -42,7 +36,8 @@ esp_err_t send_file(httpd_req_t *req, std::string filename)
|
||||
std::string _filename_temp = std::string(filename) + ".gz";
|
||||
|
||||
// Checks whether the file is available as .gz
|
||||
if (stat(_filename_temp.c_str(), &file_stat) == 0) {
|
||||
if (stat(_filename_temp.c_str(), &file_stat) == 0)
|
||||
{
|
||||
filename = _filename_temp;
|
||||
|
||||
ESP_LOGD(TAG, "new filename: %s", filename.c_str());
|
||||
@@ -50,7 +45,8 @@ esp_err_t send_file(httpd_req_t *req, std::string filename)
|
||||
}
|
||||
|
||||
FILE *fd = fopen(filename.c_str(), "r");
|
||||
if (!fd) {
|
||||
if (!fd)
|
||||
{
|
||||
ESP_LOGE(TAG, "Failed to read file: %s", filename.c_str());
|
||||
|
||||
/* Respond with 404 Error */
|
||||
@@ -73,22 +69,27 @@ esp_err_t send_file(httpd_req_t *req, std::string filename)
|
||||
endsWith(filename, ".png") ||
|
||||
endsWith(filename, ".gif") ||
|
||||
// endsWith(filename, ".zip") ||
|
||||
endsWith(filename, ".gz")) {
|
||||
if (filename == "/sdcard/html/setup.html") {
|
||||
endsWith(filename, ".gz"))
|
||||
{
|
||||
if (filename == "/sdcard/html/setup.html")
|
||||
{
|
||||
httpd_resp_set_hdr(req, "Clear-Site-Data", "\"*\"");
|
||||
set_content_type_from_file(req, filename.c_str());
|
||||
}
|
||||
else if (_gz_file_exists) {
|
||||
else if (_gz_file_exists)
|
||||
{
|
||||
httpd_resp_set_hdr(req, "Cache-Control", "max-age=43200");
|
||||
httpd_resp_set_hdr(req, "Content-Encoding", "gzip");
|
||||
set_content_type_from_file(req, _filename_old.c_str());
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
httpd_resp_set_hdr(req, "Cache-Control", "max-age=43200");
|
||||
set_content_type_from_file(req, filename.c_str());
|
||||
}
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
set_content_type_from_file(req, filename.c_str());
|
||||
}
|
||||
|
||||
@@ -96,12 +97,14 @@ esp_err_t send_file(httpd_req_t *req, std::string filename)
|
||||
char *chunk = scratch;
|
||||
size_t chunksize;
|
||||
|
||||
do {
|
||||
do
|
||||
{
|
||||
/* Read file in chunks into the scratch buffer */
|
||||
chunksize = fread(chunk, 1, SERVER_HELPER_SCRATCH_BUFSIZE, fd);
|
||||
|
||||
/* Send the buffer contents as HTTP response chunk */
|
||||
if (httpd_resp_send_chunk(req, chunk, chunksize) != ESP_OK) {
|
||||
if (httpd_resp_send_chunk(req, chunk, chunksize) != ESP_OK)
|
||||
{
|
||||
fclose(fd);
|
||||
ESP_LOGE(TAG, "File sending failed!");
|
||||
|
||||
@@ -126,21 +129,24 @@ esp_err_t send_file(httpd_req_t *req, std::string filename)
|
||||
|
||||
/* Copies the full path into destination buffer and returns
|
||||
* pointer to path (skipping the preceding base path) */
|
||||
const char* get_path_from_uri(char *dest, const char *base_path, const char *uri, size_t destsize)
|
||||
const char *get_path_from_uri(char *dest, const char *base_path, const char *uri, size_t destsize)
|
||||
{
|
||||
const size_t base_pathlen = strlen(base_path);
|
||||
size_t pathlen = strlen(uri);
|
||||
|
||||
const char *quest = strchr(uri, '?');
|
||||
if (quest) {
|
||||
if (quest)
|
||||
{
|
||||
pathlen = MIN(pathlen, quest - uri);
|
||||
}
|
||||
const char *hash = strchr(uri, '#');
|
||||
if (hash) {
|
||||
if (hash)
|
||||
{
|
||||
pathlen = MIN(pathlen, hash - uri);
|
||||
}
|
||||
|
||||
if (base_pathlen + pathlen + 1 > destsize) {
|
||||
if (base_pathlen + pathlen + 1 > destsize)
|
||||
{
|
||||
/* Full path string won't fit into destination buffer */
|
||||
return NULL;
|
||||
}
|
||||
@@ -156,43 +162,56 @@ const char* get_path_from_uri(char *dest, const char *base_path, const char *uri
|
||||
/* Set HTTP response content type according to file extension */
|
||||
esp_err_t set_content_type_from_file(httpd_req_t *req, const char *filename)
|
||||
{
|
||||
if (IS_FILE_EXT(filename, ".pdf")) {
|
||||
if (IS_FILE_EXT(filename, ".pdf"))
|
||||
{
|
||||
return httpd_resp_set_type(req, "application/x-pdf");
|
||||
}
|
||||
else if (IS_FILE_EXT(filename, ".htm")) {
|
||||
else if (IS_FILE_EXT(filename, ".htm"))
|
||||
{
|
||||
return httpd_resp_set_type(req, "text/html");
|
||||
}
|
||||
else if (IS_FILE_EXT(filename, ".html")) {
|
||||
else if (IS_FILE_EXT(filename, ".html"))
|
||||
{
|
||||
return httpd_resp_set_type(req, "text/html");
|
||||
}
|
||||
else if (IS_FILE_EXT(filename, ".jpeg")) {
|
||||
else if (IS_FILE_EXT(filename, ".jpeg"))
|
||||
{
|
||||
return httpd_resp_set_type(req, "image/jpeg");
|
||||
}
|
||||
else if (IS_FILE_EXT(filename, ".jpg")) {
|
||||
else if (IS_FILE_EXT(filename, ".jpg"))
|
||||
{
|
||||
return httpd_resp_set_type(req, "image/jpeg");
|
||||
}
|
||||
else if (IS_FILE_EXT(filename, ".gif")) {
|
||||
else if (IS_FILE_EXT(filename, ".gif"))
|
||||
{
|
||||
return httpd_resp_set_type(req, "image/gif");
|
||||
}
|
||||
else if (IS_FILE_EXT(filename, ".png")) {
|
||||
else if (IS_FILE_EXT(filename, ".png"))
|
||||
{
|
||||
return httpd_resp_set_type(req, "image/png");
|
||||
}
|
||||
else if (IS_FILE_EXT(filename, ".ico")) {
|
||||
else if (IS_FILE_EXT(filename, ".ico"))
|
||||
{
|
||||
return httpd_resp_set_type(req, "image/x-icon");
|
||||
}
|
||||
else if (IS_FILE_EXT(filename, ".js")) {
|
||||
else if (IS_FILE_EXT(filename, ".js"))
|
||||
{
|
||||
return httpd_resp_set_type(req, "application/javascript");
|
||||
}
|
||||
else if (IS_FILE_EXT(filename, ".css")) {
|
||||
else if (IS_FILE_EXT(filename, ".css"))
|
||||
{
|
||||
return httpd_resp_set_type(req, "text/css");
|
||||
}
|
||||
else if (IS_FILE_EXT(filename, ".xml")) {
|
||||
else if (IS_FILE_EXT(filename, ".xml"))
|
||||
{
|
||||
return httpd_resp_set_type(req, "text/xml");
|
||||
}
|
||||
else if (IS_FILE_EXT(filename, ".zip")) {
|
||||
else if (IS_FILE_EXT(filename, ".zip"))
|
||||
{
|
||||
return httpd_resp_set_type(req, "application/x-zip");
|
||||
}
|
||||
else if (IS_FILE_EXT(filename, ".gz")) {
|
||||
else if (IS_FILE_EXT(filename, ".gz"))
|
||||
{
|
||||
return httpd_resp_set_type(req, "application/x-gzip");
|
||||
}
|
||||
|
||||
|
||||
@@ -3,15 +3,13 @@
|
||||
#ifndef SERVERHELP_H
|
||||
#define SERVERHELP_H
|
||||
|
||||
#include "defines.h"
|
||||
|
||||
#include <string>
|
||||
//#include <sys/param.h>
|
||||
#include "esp_http_server.h"
|
||||
|
||||
|
||||
const char* get_path_from_uri(char *dest, const char *base_path, const char *uri, size_t destsize);
|
||||
|
||||
esp_err_t send_file(httpd_req_t *req, std::string filename);
|
||||
|
||||
esp_err_t set_content_type_from_file(httpd_req_t *req, const char *filename);
|
||||
|
||||
#endif //SERVERHELP_H
|
||||
@@ -1,80 +1,74 @@
|
||||
#include "defines.h"
|
||||
|
||||
#include "server_ota.h"
|
||||
|
||||
#include <string>
|
||||
#include "string.h"
|
||||
#include <string.h>
|
||||
|
||||
/* TODO Rethink the usage of the int watchdog. It is no longer to be used, see
|
||||
https://docs.espressif.com/projects/esp-idf/en/latest/esp32/migration-guides/release-5.x/5.0/system.html?highlight=esp_int_wdt */
|
||||
#include "esp_private/esp_int_wdt.h"
|
||||
#include <esp_private/esp_int_wdt.h>
|
||||
|
||||
#include <esp_task_wdt.h>
|
||||
|
||||
|
||||
#include <string.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_log.h"
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <esp_system.h>
|
||||
#include <esp_log.h>
|
||||
#include <esp_ota_ops.h>
|
||||
#include "esp_http_client.h"
|
||||
#include "esp_flash_partitions.h"
|
||||
#include "esp_partition.h"
|
||||
#include <esp_http_client.h>
|
||||
#include <esp_flash_partitions.h>
|
||||
#include <esp_partition.h>
|
||||
#include <nvs.h>
|
||||
#include "esp_app_format.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "driver/gpio.h"
|
||||
// #include "protocol_examples_common.h"
|
||||
#include "errno.h"
|
||||
#include <esp_app_format.h>
|
||||
#include <nvs_flash.h>
|
||||
#include <driver/gpio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "MainFlowControl.h"
|
||||
#include "server_file.h"
|
||||
#include "server_GPIO.h"
|
||||
#ifdef ENABLE_MQTT
|
||||
#include "interface_mqtt.h"
|
||||
#endif //ENABLE_MQTT
|
||||
#include "interface_mqtt.h"
|
||||
#include "ClassControllCamera.h"
|
||||
#include "connect_wlan.h"
|
||||
|
||||
#include "connect_wifi_sta.h"
|
||||
|
||||
#include "ClassLogFile.h"
|
||||
|
||||
#include "Helper.h"
|
||||
#include "statusled.h"
|
||||
#include "basic_auth.h"
|
||||
#include "../../include/defines.h"
|
||||
|
||||
/*an ota data write buffer ready to write to the flash*/
|
||||
static char ota_write_data[SERVER_OTA_SCRATCH_BUFSIZE + 1] = { 0 };
|
||||
static char ota_write_data[SERVER_OTA_SCRATCH_BUFSIZE + 1] = {0};
|
||||
|
||||
static const char *TAG = "OTA";
|
||||
|
||||
esp_err_t handler_reboot(httpd_req_t *req);
|
||||
static bool ota_update_task(std::string fn);
|
||||
|
||||
std::string _file_name_update;
|
||||
std::string file_name_update;
|
||||
bool initial_setup = false;
|
||||
|
||||
|
||||
static void infinite_loop(void)
|
||||
{
|
||||
int i = 0;
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "When a new firmware is available on the server, press the reset button to download it");
|
||||
while(1) {
|
||||
while (1)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Waiting for a new firmware... (" + to_string(++i) + ")");
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void task_do_Update_ZIP(void *pvParameter)
|
||||
{
|
||||
StatusLED(AP_OR_OTA, 1, true); // Signaling an OTA update
|
||||
set_status_led(AP_OR_OTA, 1, true); // Signaling an OTA update
|
||||
|
||||
std::string filetype = toUpper(getFileType(_file_name_update));
|
||||
std::string filetype = to_upper(get_file_type(file_name_update));
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "File: " + _file_name_update + " Filetype: " + filetype);
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "File: " + file_name_update + " Filetype: " + filetype);
|
||||
|
||||
if (filetype == "ZIP")
|
||||
{
|
||||
@@ -86,21 +80,23 @@ void task_do_Update_ZIP(void *pvParameter)
|
||||
outbin = "/sdcard/firmware";
|
||||
|
||||
/* Remove the old and tmp html folder in case they still exist */
|
||||
removeFolder(outHtmlTmp.c_str(), TAG);
|
||||
removeFolder(outHtmlOld.c_str(), TAG);
|
||||
remove_folder(outHtmlTmp.c_str(), TAG);
|
||||
remove_folder(outHtmlOld.c_str(), TAG);
|
||||
|
||||
/* Extract the ZIP file. The content of the html folder gets extracted to the temporar folder html-temp. */
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Extracting ZIP file " + _file_name_update + "...");
|
||||
retfirmware = unzip_new(_file_name_update, outHtmlTmp+"/", outHtml+"/", outbin+"/", "/sdcard/", initial_setup);
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Files unzipped.");
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Extracting ZIP file " + file_name_update + "...");
|
||||
retfirmware = unzip_new(file_name_update, outHtmlTmp + "/", outHtml + "/", outbin + "/", "/sdcard/", initial_setup);
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Files unzipped.");
|
||||
|
||||
/* ZIP file got extracted, replace the old html folder with the new one */
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Renaming folder " + outHtml + " to " + outHtmlOld + "...");
|
||||
RenameFolder(outHtml, outHtmlOld);
|
||||
rename_folder(outHtml, outHtmlOld);
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Renaming folder " + outHtmlTmp + " to " + outHtml + "...");
|
||||
RenameFolder(outHtmlTmp, outHtml);
|
||||
rename_folder(outHtmlTmp, outHtml);
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Deleting folder " + outHtmlOld + "...");
|
||||
removeFolder(outHtmlOld.c_str(), TAG);
|
||||
remove_folder(outHtmlOld.c_str(), TAG);
|
||||
|
||||
if (retfirmware.length() > 0)
|
||||
{
|
||||
@@ -110,58 +106,61 @@ void task_do_Update_ZIP(void *pvParameter)
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Trigger reboot due to firmware update");
|
||||
doRebootOTA();
|
||||
} else if (filetype == "BIN")
|
||||
}
|
||||
else if (filetype == "BIN")
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Do firmware update - file: " + _file_name_update);
|
||||
ota_update_task(_file_name_update);
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Do firmware update - file: " + file_name_update);
|
||||
ota_update_task(file_name_update);
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Trigger reboot due to firmware update");
|
||||
doRebootOTA();
|
||||
}
|
||||
else
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Only ZIP-Files support for update during startup!");
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Only ZIP-Files support for update during startup!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CheckUpdate()
|
||||
bool CheckOTAUpdateAvailability(void)
|
||||
{
|
||||
FILE *pfile;
|
||||
if ((pfile = fopen("/sdcard/update.txt", "r")) == NULL)
|
||||
FILE *pFile = fopen("/sdcard/update.txt", "r");
|
||||
if (pFile == NULL)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "No pending update");
|
||||
return;
|
||||
}
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "CheckOTAUpdateAvailability() - No pending update");
|
||||
return false;
|
||||
}
|
||||
|
||||
char zw[1024] = "";
|
||||
fgets(zw, 1024, pfile);
|
||||
_file_name_update = std::string(zw);
|
||||
if (fgets(zw, 1024, pfile))
|
||||
{
|
||||
std::string _szw = std::string(zw);
|
||||
if (_szw == "init")
|
||||
char data_temp[1024] = "";
|
||||
fgets(data_temp, 1024, pFile);
|
||||
file_name_update = std::string(data_temp);
|
||||
|
||||
if (fgets(data_temp, 1024, pFile))
|
||||
{
|
||||
std::string _data_temp = std::string(data_temp);
|
||||
if (_data_temp == "init")
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Inital Setup triggered");
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "CheckOTAUpdateAvailability() - Inital Setup triggered");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fclose(pfile);
|
||||
DeleteFile("/sdcard/update.txt"); // Prevent Boot Loop!!!
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Start update process (" + _file_name_update + ")");
|
||||
fclose(pFile);
|
||||
delete_file("/sdcard/update.txt"); // Prevent Boot Loop!!!
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "CheckOTAUpdateAvailability() - Start update process (" + file_name_update + ")");
|
||||
|
||||
|
||||
xTaskCreate(&task_do_Update_ZIP, "task_do_Update_ZIP", configMINIMAL_STACK_SIZE * 35, NULL, tskIDLE_PRIORITY+1, NULL);
|
||||
while(1) { // wait until reboot within task_do_Update_ZIP
|
||||
xTaskCreate(&task_do_Update_ZIP, "task_do_Update_ZIP", configMINIMAL_STACK_SIZE * 35, NULL, tskIDLE_PRIORITY + 1, NULL);
|
||||
while (1)
|
||||
{
|
||||
// wait until reboot within task_do_Update_ZIP
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ota_update_task(std::string fn)
|
||||
{
|
||||
esp_err_t err;
|
||||
/* update handle : set by esp_ota_begin(), must be freed via esp_ota_end() */
|
||||
esp_ota_handle_t update_handle = 0 ;
|
||||
esp_ota_handle_t update_handle = 0;
|
||||
const esp_partition_t *update_partition = NULL;
|
||||
|
||||
ESP_LOGI(TAG, "Starting OTA update");
|
||||
@@ -169,19 +168,16 @@ static bool ota_update_task(std::string fn)
|
||||
const esp_partition_t *configured = esp_ota_get_boot_partition();
|
||||
const esp_partition_t *running = esp_ota_get_running_partition();
|
||||
|
||||
if (configured != running) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Configured OTA boot partition at offset " + to_string(configured->address) +
|
||||
", but running from offset " + to_string(running->address));
|
||||
if (configured != running)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Configured OTA boot partition at offset " + to_string(configured->address) + ", but running from offset " + to_string(running->address));
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "(This can happen if either the OTA boot data or preferred boot image become somehow corrupted.)");
|
||||
}
|
||||
ESP_LOGI(TAG, "Running partition type %d subtype %d (offset 0x%08x)",
|
||||
running->type, running->subtype, (unsigned int)running->address);
|
||||
|
||||
|
||||
update_partition = esp_ota_get_next_update_partition(NULL);
|
||||
ESP_LOGI(TAG, "Writing to partition subtype %d at offset 0x%x",
|
||||
update_partition->subtype, (unsigned int)update_partition->address);
|
||||
// assert(update_partition != NULL);
|
||||
ESP_LOGI(TAG, "Writing to partition subtype %d at offset 0x%x", update_partition->subtype, (unsigned int)update_partition->address);
|
||||
|
||||
int binary_file_length = 0;
|
||||
|
||||
@@ -190,79 +186,89 @@ static bool ota_update_task(std::string fn)
|
||||
|
||||
int data_read;
|
||||
|
||||
FILE* f = fopen(fn.c_str(), "rb"); // previously only "r
|
||||
FILE *f = fopen(fn.c_str(), "rb"); // previously only "r
|
||||
|
||||
if (f == NULL) { // File does not exist
|
||||
if (f == NULL)
|
||||
{
|
||||
// File does not exist
|
||||
return false;
|
||||
}
|
||||
|
||||
data_read = fread(ota_write_data, 1, SERVER_OTA_SCRATCH_BUFSIZE, f);
|
||||
|
||||
while (data_read > 0) {
|
||||
if (data_read < 0) {
|
||||
while (data_read > 0)
|
||||
{
|
||||
if (data_read < 0)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Error: SSL data read error");
|
||||
return false;
|
||||
} else if (data_read > 0) {
|
||||
if (image_header_was_checked == false) {
|
||||
}
|
||||
else if (data_read > 0)
|
||||
{
|
||||
if (image_header_was_checked == false)
|
||||
{
|
||||
esp_app_desc_t new_app_info;
|
||||
if (data_read > sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t) + sizeof(esp_app_desc_t)) {
|
||||
if (data_read > sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t) + sizeof(esp_app_desc_t))
|
||||
{
|
||||
// check current version with downloading
|
||||
memcpy(&new_app_info, &ota_write_data[sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t)], sizeof(esp_app_desc_t));
|
||||
ESP_LOGI(TAG, "New firmware version: %s", new_app_info.version);
|
||||
|
||||
esp_app_desc_t running_app_info;
|
||||
if (esp_ota_get_partition_description(running, &running_app_info) == ESP_OK) {
|
||||
if (esp_ota_get_partition_description(running, &running_app_info) == ESP_OK)
|
||||
{
|
||||
ESP_LOGI(TAG, "Running firmware version: %s", running_app_info.version);
|
||||
}
|
||||
|
||||
const esp_partition_t* last_invalid_app = esp_ota_get_last_invalid_partition();
|
||||
const esp_partition_t *last_invalid_app = esp_ota_get_last_invalid_partition();
|
||||
esp_app_desc_t invalid_app_info;
|
||||
if (esp_ota_get_partition_description(last_invalid_app, &invalid_app_info) == ESP_OK) {
|
||||
if (esp_ota_get_partition_description(last_invalid_app, &invalid_app_info) == ESP_OK)
|
||||
{
|
||||
ESP_LOGI(TAG, "Last invalid firmware version: %s", invalid_app_info.version);
|
||||
}
|
||||
|
||||
// check current version with last invalid partition
|
||||
if (last_invalid_app != NULL) {
|
||||
if (memcmp(invalid_app_info.version, new_app_info.version, sizeof(new_app_info.version)) == 0) {
|
||||
if (last_invalid_app != NULL)
|
||||
{
|
||||
if (memcmp(invalid_app_info.version, new_app_info.version, sizeof(new_app_info.version)) == 0)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "New version is the same as invalid version");
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Previously, there was an attempt to launch the firmware with " +
|
||||
string(invalid_app_info.version) + " version, but it failed");
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Previously, there was an attempt to launch the firmware with " + string(invalid_app_info.version) + " version, but it failed");
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "The firmware has been rolled back to the previous version");
|
||||
infinite_loop();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
if (memcmp(new_app_info.version, running_app_info.version, sizeof(new_app_info.version)) == 0) {
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Current running version is the same as a new. We will not continue the update");
|
||||
infinite_loop();
|
||||
}
|
||||
*/
|
||||
image_header_was_checked = true;
|
||||
|
||||
err = esp_ota_begin(update_partition, OTA_SIZE_UNKNOWN, &update_handle);
|
||||
if (err != ESP_OK) {
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_ota_begin failed (" + string(esp_err_to_name(err)) + ")");
|
||||
return false;
|
||||
}
|
||||
ESP_LOGI(TAG, "esp_ota_begin succeeded");
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "received package is not fit len");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
err = esp_ota_write( update_handle, (const void *)ota_write_data, data_read);
|
||||
if (err != ESP_OK) {
|
||||
err = esp_ota_write(update_handle, (const void *)ota_write_data, data_read);
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
binary_file_length += data_read;
|
||||
ESP_LOGD(TAG, "Written image length %d", binary_file_length);
|
||||
} else if (data_read == 0) {
|
||||
//
|
||||
// * As esp_http_client_read never returns negative error code, we rely on
|
||||
// * `errno` to check for underlying transport connectivity closure if any
|
||||
//
|
||||
if (errno == ECONNRESET || errno == ENOTCONN) {
|
||||
}
|
||||
else if (data_read == 0)
|
||||
{
|
||||
// * As esp_http_client_read never returns negative error code, we rely on
|
||||
// * `errno` to check for underlying transport connectivity closure if any
|
||||
if (errno == ECONNRESET || errno == ENOTCONN)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Connection closed, errno = " + to_string(errno));
|
||||
break;
|
||||
}
|
||||
@@ -274,8 +280,10 @@ static bool ota_update_task(std::string fn)
|
||||
ESP_LOGI(TAG, "Total Write binary data length: %d", binary_file_length);
|
||||
|
||||
err = esp_ota_end(update_handle);
|
||||
if (err != ESP_OK) {
|
||||
if (err == ESP_ERR_OTA_VALIDATE_FAILED) {
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
if (err == ESP_ERR_OTA_VALIDATE_FAILED)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Image validation failed, image is corrupted");
|
||||
}
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_ota_end failed (" + string(esp_err_to_name(err)) + ")!");
|
||||
@@ -283,52 +291,48 @@ static bool ota_update_task(std::string fn)
|
||||
}
|
||||
|
||||
err = esp_ota_set_boot_partition(update_partition);
|
||||
if (err != ESP_OK) {
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_ota_set_boot_partition failed (" + string(esp_err_to_name(err)) + ")!");
|
||||
|
||||
}
|
||||
// ESP_LOGI(TAG, "Prepare to restart system!");
|
||||
// esp_restart();
|
||||
|
||||
return true ;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static void print_sha256 (const uint8_t *image_hash, const char *label)
|
||||
static void print_sha256(const uint8_t *image_hash, const char *label)
|
||||
{
|
||||
char hash_print[HASH_LEN * 2 + 1];
|
||||
hash_print[HASH_LEN * 2] = 0;
|
||||
for (int i = 0; i < HASH_LEN; ++i) {
|
||||
for (int i = 0; i < HASH_LEN; ++i)
|
||||
{
|
||||
sprintf(&hash_print[i * 2], "%02x", image_hash[i]);
|
||||
}
|
||||
ESP_LOGI(TAG, "%s: %s", label, hash_print);
|
||||
}
|
||||
|
||||
|
||||
static bool diagnostic(void)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void CheckOTAUpdate(void)
|
||||
void CheckOTAUpdateStatus(void)
|
||||
{
|
||||
ESP_LOGI(TAG, "Start CheckOTAUpdateCheck...");
|
||||
ESP_LOGI(TAG, "Start CheckOTAUpdateStatus...");
|
||||
|
||||
uint8_t sha_256[HASH_LEN] = { 0 };
|
||||
uint8_t sha_256[HASH_LEN] = {0};
|
||||
esp_partition_t partition;
|
||||
|
||||
// get sha256 digest for the partition table
|
||||
partition.address = ESP_PARTITION_TABLE_OFFSET;
|
||||
partition.size = ESP_PARTITION_TABLE_MAX_LEN;
|
||||
partition.type = ESP_PARTITION_TYPE_DATA;
|
||||
partition.address = ESP_PARTITION_TABLE_OFFSET;
|
||||
partition.size = ESP_PARTITION_TABLE_MAX_LEN;
|
||||
partition.type = ESP_PARTITION_TYPE_DATA;
|
||||
esp_partition_get_sha256(&partition, sha_256);
|
||||
print_sha256(sha_256, "SHA-256 for the partition table: ");
|
||||
|
||||
// get sha256 digest for bootloader
|
||||
partition.address = ESP_BOOTLOADER_OFFSET;
|
||||
partition.size = ESP_PARTITION_TABLE_OFFSET;
|
||||
partition.type = ESP_PARTITION_TYPE_APP;
|
||||
partition.address = ESP_BOOTLOADER_OFFSET;
|
||||
partition.size = ESP_PARTITION_TABLE_OFFSET;
|
||||
partition.type = ESP_PARTITION_TYPE_APP;
|
||||
esp_partition_get_sha256(&partition, sha_256);
|
||||
print_sha256(sha_256, "SHA-256 for bootloader: ");
|
||||
|
||||
@@ -341,40 +345,50 @@ void CheckOTAUpdate(void)
|
||||
esp_err_t res_stat_partition = esp_ota_get_state_partition(running, &ota_state);
|
||||
switch (res_stat_partition)
|
||||
{
|
||||
case ESP_OK:
|
||||
ESP_LOGD(TAG, "CheckOTAUpdate Partition: ESP_OK");
|
||||
if (esp_ota_get_state_partition(running, &ota_state) == ESP_OK) {
|
||||
if (ota_state == ESP_OTA_IMG_PENDING_VERIFY) {
|
||||
// run diagnostic function ...
|
||||
bool diagnostic_is_ok = diagnostic();
|
||||
if (diagnostic_is_ok) {
|
||||
ESP_LOGI(TAG, "Diagnostics completed successfully! Continuing execution...");
|
||||
esp_ota_mark_app_valid_cancel_rollback();
|
||||
} else {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Diagnostics failed! Start rollback to the previous version...");
|
||||
esp_ota_mark_app_invalid_rollback_and_reboot();
|
||||
}
|
||||
case ESP_OK:
|
||||
ESP_LOGD(TAG, "CheckOTAUpdateStatus Partition: ESP_OK");
|
||||
if (esp_ota_get_state_partition(running, &ota_state) == ESP_OK)
|
||||
{
|
||||
if (ota_state == ESP_OTA_IMG_PENDING_VERIFY)
|
||||
{
|
||||
// run diagnostic function ...
|
||||
bool diagnostic_is_ok = diagnostic();
|
||||
if (diagnostic_is_ok)
|
||||
{
|
||||
ESP_LOGI(TAG, "Diagnostics completed successfully! Continuing execution...");
|
||||
esp_ota_mark_app_valid_cancel_rollback();
|
||||
}
|
||||
else
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Diagnostics failed! Start rollback to the previous version...");
|
||||
esp_ota_mark_app_invalid_rollback_and_reboot();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ESP_ERR_INVALID_ARG:
|
||||
ESP_LOGD(TAG, "CheckOTAUpdate Partition: ESP_ERR_INVALID_ARG");
|
||||
break;
|
||||
case ESP_ERR_NOT_SUPPORTED:
|
||||
ESP_LOGD(TAG, "CheckOTAUpdate Partition: ESP_ERR_NOT_SUPPORTED");
|
||||
break;
|
||||
case ESP_ERR_NOT_FOUND:
|
||||
ESP_LOGD(TAG, "CheckOTAUpdate Partition: ESP_ERR_NOT_FOUND");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case ESP_ERR_INVALID_ARG:
|
||||
ESP_LOGD(TAG, "CheckOTAUpdateStatus Partition: ESP_ERR_INVALID_ARG");
|
||||
break;
|
||||
case ESP_ERR_NOT_SUPPORTED:
|
||||
ESP_LOGD(TAG, "CheckOTAUpdateStatus Partition: ESP_ERR_NOT_SUPPORTED");
|
||||
break;
|
||||
case ESP_ERR_NOT_FOUND:
|
||||
ESP_LOGD(TAG, "CheckOTAUpdateStatus Partition: ESP_ERR_NOT_FOUND");
|
||||
break;
|
||||
}
|
||||
if (esp_ota_get_state_partition(running, &ota_state) == ESP_OK) {
|
||||
if (ota_state == ESP_OTA_IMG_PENDING_VERIFY) {
|
||||
if (esp_ota_get_state_partition(running, &ota_state) == ESP_OK)
|
||||
{
|
||||
if (ota_state == ESP_OTA_IMG_PENDING_VERIFY)
|
||||
{
|
||||
// run diagnostic function ...
|
||||
bool diagnostic_is_ok = diagnostic();
|
||||
if (diagnostic_is_ok) {
|
||||
if (diagnostic_is_ok)
|
||||
{
|
||||
ESP_LOGI(TAG, "Diagnostics completed successfully! Continuing execution...");
|
||||
esp_ota_mark_app_valid_cancel_rollback();
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Diagnostics failed! Start rollback to the previous version...");
|
||||
esp_ota_mark_app_invalid_rollback_and_reboot();
|
||||
}
|
||||
@@ -382,13 +396,8 @@ void CheckOTAUpdate(void)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
esp_err_t handler_ota_update(httpd_req_t *req)
|
||||
{
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("handler_ota_update - Start");
|
||||
#endif
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "handler_ota_update");
|
||||
char _query[200];
|
||||
char _filename[100];
|
||||
@@ -418,7 +427,6 @@ esp_err_t handler_ota_update(httpd_req_t *req)
|
||||
_file_del = true;
|
||||
ESP_LOGD(TAG, "Delete Default File: %s", fn.c_str());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (_task.compare("emptyfirmwaredir") == 0)
|
||||
@@ -440,7 +448,7 @@ esp_err_t handler_ota_update(httpd_req_t *req)
|
||||
|
||||
if (_task.compare("update") == 0)
|
||||
{
|
||||
std::string filetype = toUpper(getFileType(fn));
|
||||
std::string filetype = to_upper(get_file_type(fn));
|
||||
if (filetype.length() == 0)
|
||||
{
|
||||
std::string zw = "Update failed - no file specified (zip, bin, tfl, tlite)";
|
||||
@@ -451,21 +459,20 @@ esp_err_t handler_ota_update(httpd_req_t *req)
|
||||
|
||||
if ((filetype == "TFLITE") || (filetype == "TFL"))
|
||||
{
|
||||
std::string out = "/sdcard/config/" + getFileFullFileName(fn);
|
||||
DeleteFile(out);
|
||||
CopyFile(fn, out);
|
||||
DeleteFile(fn);
|
||||
std::string out = "/sdcard/config/" + get_file_full_filename(fn);
|
||||
delete_file(out);
|
||||
copy_file(fn, out);
|
||||
delete_file(fn);
|
||||
|
||||
const char* resp_str = "Neural Network File copied.";
|
||||
const char *resp_str = "Neural Network File copied.";
|
||||
httpd_resp_sendstr_chunk(req, resp_str);
|
||||
httpd_resp_sendstr_chunk(req, NULL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
if ((filetype == "ZIP") || (filetype == "BIN"))
|
||||
{
|
||||
FILE *pfile;
|
||||
FILE *pfile;
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Update for reboot");
|
||||
pfile = fopen("/sdcard/update.txt", "w");
|
||||
fwrite(fn.c_str(), fn.length(), 1, pfile);
|
||||
@@ -476,43 +483,14 @@ esp_err_t handler_ota_update(httpd_req_t *req)
|
||||
httpd_resp_sendstr_chunk(req, NULL);
|
||||
ESP_LOGD(TAG, "Send reboot");
|
||||
return ESP_OK;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
if (filetype == "BIN")
|
||||
{
|
||||
const char* resp_str;
|
||||
|
||||
DeleteMainFlowTask();
|
||||
gpio_handler_deinit();
|
||||
if (ota_update_task(fn))
|
||||
{
|
||||
std::string zw = "reboot\n";
|
||||
httpd_resp_sendstr_chunk(req, zw.c_str());
|
||||
httpd_resp_sendstr_chunk(req, NULL);
|
||||
ESP_LOGD(TAG, "Send reboot");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
resp_str = "Error during Firmware Update!!!\nPlease check output of console.";
|
||||
httpd_resp_send(req, resp_str, strlen(resp_str));
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("handler_ota_update - Done");
|
||||
#endif
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
*/
|
||||
|
||||
std::string zw = "Update failed - no valid file specified (zip, bin, tfl, tlite)!";
|
||||
httpd_resp_sendstr_chunk(req, zw.c_str());
|
||||
httpd_resp_sendstr_chunk(req, NULL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
if (_task.compare("unziphtml") == 0)
|
||||
{
|
||||
ESP_LOGD(TAG, "Task unziphtml");
|
||||
@@ -523,7 +501,7 @@ esp_err_t handler_ota_update(httpd_req_t *req)
|
||||
|
||||
delete_all_in_directory(out);
|
||||
|
||||
unzip(in, out+"/");
|
||||
unzip(in, out + "/");
|
||||
zw = "Web Interface Update Successfull!\nNo reboot necessary";
|
||||
httpd_resp_send(req, zw.c_str(), strlen(zw.c_str()));
|
||||
httpd_resp_sendstr_chunk(req, NULL);
|
||||
@@ -536,7 +514,8 @@ esp_err_t handler_ota_update(httpd_req_t *req)
|
||||
struct stat file_stat;
|
||||
int _result = stat(fn.c_str(), &file_stat);
|
||||
ESP_LOGD(TAG, "Ergebnis %d\n", _result);
|
||||
if (_result == 0) {
|
||||
if (_result == 0)
|
||||
{
|
||||
ESP_LOGD(TAG, "Deleting file: %s", fn.c_str());
|
||||
/* Delete file */
|
||||
unlink(fn.c_str());
|
||||
@@ -560,159 +539,144 @@ esp_err_t handler_ota_update(httpd_req_t *req)
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "ota without parameter - should not be the case!");
|
||||
|
||||
/*
|
||||
const char* resp_str;
|
||||
|
||||
DeleteMainFlowTask();
|
||||
gpio_handler_deinit();
|
||||
if (ota_update_task(fn))
|
||||
{
|
||||
resp_str = "Firmware Update Successfull! You can restart now.";
|
||||
}
|
||||
else
|
||||
{
|
||||
resp_str = "Error during Firmware Update!!! Please check console output.";
|
||||
}
|
||||
|
||||
httpd_resp_send(req, resp_str, strlen(resp_str));
|
||||
*/
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("handler_ota_update - Done");
|
||||
#endif
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
void hard_restart()
|
||||
void hard_restart(void)
|
||||
{
|
||||
esp_task_wdt_config_t twdt_config = {
|
||||
.timeout_ms = 1,
|
||||
.idle_core_mask = (1 << portNUM_PROCESSORS) - 1, // Bitmask of all cores
|
||||
.trigger_panic = true,
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_task_wdt_init(&twdt_config));
|
||||
esp_task_wdt_config_t twdt_config = {
|
||||
.timeout_ms = 1,
|
||||
.idle_core_mask = (1 << portNUM_PROCESSORS) - 1, // Bitmask of all cores
|
||||
.trigger_panic = true,
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_task_wdt_init(&twdt_config));
|
||||
|
||||
esp_task_wdt_add(NULL);
|
||||
while(true);
|
||||
esp_task_wdt_add(NULL);
|
||||
while (true)
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
void task_reboot(void *DeleteMainFlow)
|
||||
{
|
||||
// write a reboot, to identify a reboot by purpouse
|
||||
FILE* pfile = fopen("/sdcard/reboot.txt", "w");
|
||||
std::string _s_zw= "reboot";
|
||||
fwrite(_s_zw.c_str(), strlen(_s_zw.c_str()), 1, pfile);
|
||||
FILE *pfile = fopen("/sdcard/reboot.txt", "w");
|
||||
std::string data_temp = "reboot";
|
||||
fwrite(data_temp.c_str(), strlen(data_temp.c_str()), 1, pfile);
|
||||
fclose(pfile);
|
||||
|
||||
vTaskDelay(3000 / portTICK_PERIOD_MS);
|
||||
|
||||
if ((bool)DeleteMainFlow) {
|
||||
DeleteMainFlowTask(); // Kill autoflow task if executed in extra task, if not don't kill parent task
|
||||
if ((bool)DeleteMainFlow)
|
||||
{
|
||||
DeleteMainFlowTask(); // Kill autoflow task if executed in extra task, if not don't kill parent task
|
||||
}
|
||||
|
||||
Camera.LightOnOff(false);
|
||||
StatusLEDOff();
|
||||
Camera.set_camera_deep_sleep(false);
|
||||
esp_camera_return_all();
|
||||
Camera.set_flash_light_on_off(false);
|
||||
set_status_led_off();
|
||||
|
||||
/* Stop service tasks */
|
||||
#ifdef ENABLE_MQTT
|
||||
MQTTdestroy_client(true);
|
||||
#endif //ENABLE_MQTT
|
||||
MQTTdestroy_client(true);
|
||||
|
||||
gpio_handler_destroy();
|
||||
esp_camera_deinit();
|
||||
WIFIDestroy();
|
||||
wifi_deinit_sta();
|
||||
|
||||
vTaskDelay(3000 / portTICK_PERIOD_MS);
|
||||
esp_restart(); // Reset type: CPU reset (Reset both CPUs)
|
||||
esp_restart(); // Reset type: CPU reset (Reset both CPUs)
|
||||
|
||||
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
||||
hard_restart(); // Reset type: System reset (Triggered by watchdog), if esp_restart stalls (WDT needs to be activated)
|
||||
hard_restart(); // Reset type: System reset (Triggered by watchdog), if esp_restart stalls (WDT needs to be activated)
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Reboot failed!");
|
||||
vTaskDelete(NULL); //Delete this task if it comes to this point
|
||||
vTaskDelete(NULL); // Delete this task if it comes to this point
|
||||
}
|
||||
|
||||
|
||||
void doReboot()
|
||||
void doReboot(void)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Reboot triggered by Software (5s)");
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Reboot in 5sec");
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "doReboot() - Reboot triggered by Software (5s)");
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "doReboot() - Reboot in 5sec");
|
||||
|
||||
BaseType_t xReturned = xTaskCreate(&task_reboot, "task_reboot", configMINIMAL_STACK_SIZE * 4, (void*) true, 10, NULL);
|
||||
if( xReturned != pdPASS )
|
||||
while (*flowctrl.getActStatus() == std::string("Take Image"))
|
||||
{
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
|
||||
BaseType_t xReturned = xTaskCreate(&task_reboot, "task_reboot", configMINIMAL_STACK_SIZE * 4, (void *)true, 10, NULL);
|
||||
if (xReturned != pdPASS)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "task_reboot not created -> force reboot without killing flow");
|
||||
task_reboot((void*) false);
|
||||
task_reboot((void *)false);
|
||||
}
|
||||
vTaskDelay(10000 / portTICK_PERIOD_MS); // Prevent serving web client fetch response until system is shuting down
|
||||
}
|
||||
|
||||
|
||||
void doRebootOTA()
|
||||
void doRebootOTA(void)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Reboot in 5sec");
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "doRebootOTA() - Reboot in 5sec");
|
||||
|
||||
Camera.LightOnOff(false);
|
||||
StatusLEDOff();
|
||||
esp_camera_deinit();
|
||||
while (*flowctrl.getActStatus() == std::string("Take Image"))
|
||||
{
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
|
||||
DeleteMainFlowTask(); // Kill autoflow task if executed in extra task, if not don't kill parent task
|
||||
|
||||
Camera.set_camera_deep_sleep(false);
|
||||
esp_camera_return_all();
|
||||
Camera.set_flash_light_on_off(false);
|
||||
set_status_led_off();
|
||||
|
||||
/* Stop service tasks */
|
||||
MQTTdestroy_client(true);
|
||||
|
||||
gpio_handler_destroy();
|
||||
wifi_deinit_sta();
|
||||
|
||||
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
||||
esp_restart(); // Reset type: CPU reset (Reset both CPUs)
|
||||
esp_restart(); // Reset type: CPU reset (Reset both CPUs)
|
||||
|
||||
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
||||
hard_restart(); // Reset type: System reset (Triggered by watchdog), if esp_restart stalls (WDT needs to be activated)
|
||||
hard_restart(); // Reset type: System reset (Triggered by watchdog), if esp_restart stalls (WDT needs to be activated)
|
||||
}
|
||||
|
||||
|
||||
esp_err_t handler_reboot(httpd_req_t *req)
|
||||
{
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("handler_reboot - Start");
|
||||
#endif
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "handler_reboot");
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "!!! System will restart within 5 sec!!!");
|
||||
|
||||
std::string response =
|
||||
"<html><head><script>"
|
||||
"function m(h) {"
|
||||
"document.getElementById('t').innerHTML=h;"
|
||||
"setInterval(function (){h +='.'; document.getElementById('t').innerHTML=h;"
|
||||
"fetch('reboot_page.html',{mode: 'no-cors'}).then(r=>{parent.location.href=('index.html');})}, 1000);"
|
||||
"}</script></head></html><body style='font-family: arial'><h3 id=t></h3>"
|
||||
"<script>m('Rebooting!<br>The page will automatically reload in around 25..60s.<br><br>');</script>"
|
||||
"</body></html>";
|
||||
"function m(h) {"
|
||||
"document.getElementById('t').innerHTML=h;"
|
||||
"setInterval(function (){h +='.'; document.getElementById('t').innerHTML=h;"
|
||||
"fetch('reboot_page.html',{mode: 'no-cors'}).then(r=>{parent.location.href=('index.html');})}, 1000);"
|
||||
"}</script></head></html><body style='font-family: arial'><h3 id=t></h3>"
|
||||
"<script>m('Rebooting!<br>The page will automatically reload in around 25..60s.<br><br>');</script>"
|
||||
"</body></html>";
|
||||
|
||||
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
|
||||
httpd_resp_send(req, response.c_str(), strlen(response.c_str()));
|
||||
|
||||
doReboot();
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("handler_reboot - Done");
|
||||
#endif
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
void register_server_ota_sdcard_uri(httpd_handle_t server)
|
||||
void ota_register_uri(httpd_handle_t server)
|
||||
{
|
||||
ESP_LOGI(TAG, "Registering URI handlers");
|
||||
|
||||
httpd_uri_t camuri = { };
|
||||
camuri.method = HTTP_GET;
|
||||
camuri.uri = "/ota";
|
||||
httpd_uri_t camuri = {};
|
||||
camuri.method = HTTP_GET;
|
||||
camuri.uri = "/ota";
|
||||
camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_ota_update);
|
||||
camuri.user_ctx = (void*) "Do OTA";
|
||||
camuri.user_ctx = (void *)"Do OTA";
|
||||
httpd_register_uri_handler(server, &camuri);
|
||||
|
||||
camuri.method = HTTP_GET;
|
||||
camuri.uri = "/reboot";
|
||||
camuri.method = HTTP_GET;
|
||||
camuri.uri = "/reboot";
|
||||
camuri.handler = APPLY_BASIC_AUTH_FILTER(handler_reboot);
|
||||
camuri.user_ctx = (void*) "Reboot";
|
||||
camuri.user_ctx = (void *)"Reboot";
|
||||
httpd_register_uri_handler(server, &camuri);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef SERVEROTA_H
|
||||
#define SERVEROTA_H
|
||||
#ifndef SERVER_OTA_H
|
||||
#define SERVER_OTA_H
|
||||
|
||||
#include <esp_log.h>
|
||||
|
||||
#include <esp_http_server.h>
|
||||
#include "defines.h"
|
||||
|
||||
#include <string>
|
||||
#include <esp_log.h>
|
||||
#include <esp_http_server.h>
|
||||
|
||||
void CheckOTAUpdateStatus(void);
|
||||
bool CheckOTAUpdateAvailability(void);
|
||||
|
||||
void register_server_ota_sdcard_uri(httpd_handle_t server);
|
||||
void CheckOTAUpdate();
|
||||
void doReboot();
|
||||
void doRebootOTA();
|
||||
void hard_restart();
|
||||
void CheckUpdate();
|
||||
void doReboot(void);
|
||||
void doRebootOTA(void);
|
||||
void hard_restart(void);
|
||||
|
||||
#endif //SERVEROTA_H
|
||||
void ota_register_uri(httpd_handle_t server);
|
||||
|
||||
#endif // SERVEROTA_H
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
|
||||
|
||||
idf_component_register(SRCS ${app_sources}
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES esp_timer esp_wifi jomjol_tfliteclass jomjol_helper jomjol_controlcamera jomjol_mqtt jomjol_influxdb jomjol_webhook jomjol_fileserver_ota jomjol_image_proc jomjol_wlan openmetrics)
|
||||
INCLUDE_DIRS "." "../../include"
|
||||
REQUIRES esp_timer esp_wifi jomjol_tfliteclass jomjol_helper jomjol_controlcamera jomjol_mqtt jomjol_influxdb jomjol_webhook jomjol_fileserver_ota jomjol_image_proc jomjol_network openmetrics)
|
||||
|
||||
|
||||
|
||||
@@ -1,114 +1,192 @@
|
||||
#include "defines.h"
|
||||
|
||||
#include "ClassFlow.h"
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <string.h>
|
||||
#include "esp_log.h"
|
||||
#include "../../include/defines.h"
|
||||
|
||||
static const char *TAG = "CLASS";
|
||||
|
||||
|
||||
void ClassFlow::SetInitialParameter(void)
|
||||
{
|
||||
ListFlowControll = NULL;
|
||||
previousElement = NULL;
|
||||
|
||||
disabled = false;
|
||||
}
|
||||
|
||||
bool ClassFlow::isNewParagraph(string input)
|
||||
{
|
||||
if ((input[0] == '[') || ((input[0] == ';') && (input[1] == '[')))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ClassFlow::GetNextParagraph(FILE* pfile, string& aktparamgraph)
|
||||
{
|
||||
while (getNextLine(pfile, &aktparamgraph) && !isNewParagraph(aktparamgraph));
|
||||
|
||||
if (isNewParagraph(aktparamgraph))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
ClassFlow::ClassFlow(void)
|
||||
{
|
||||
SetInitialParameter();
|
||||
}
|
||||
|
||||
ClassFlow::ClassFlow(std::vector<ClassFlow*> * lfc)
|
||||
ClassFlow::ClassFlow(std::vector<ClassFlow *> *lfc)
|
||||
{
|
||||
SetInitialParameter();
|
||||
ListFlowControll = lfc;
|
||||
}
|
||||
|
||||
ClassFlow::ClassFlow(std::vector<ClassFlow*> * lfc, ClassFlow *_prev)
|
||||
ClassFlow::ClassFlow(std::vector<ClassFlow *> *lfc, ClassFlow *_prev)
|
||||
{
|
||||
SetInitialParameter();
|
||||
ListFlowControll = lfc;
|
||||
previousElement = _prev;
|
||||
}
|
||||
|
||||
bool ClassFlow::ReadParameter(FILE* pfile, string &aktparamgraph)
|
||||
bool ClassFlow::ReadParameter(FILE *pFile, std::string &aktparamgraph)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ClassFlow::doFlow(string time)
|
||||
bool ClassFlow::doFlow(std::string time)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
string ClassFlow::getHTMLSingleStep(string host){
|
||||
std::string ClassFlow::getHTMLSingleStep(std::string host)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string ClassFlow::GetParameterName(std::string _input)
|
||||
{
|
||||
string _param;
|
||||
int _pospunkt = _input.find_first_of(".");
|
||||
if (_pospunkt > -1)
|
||||
{
|
||||
_param = _input.substr(_pospunkt+1, _input.length() - _pospunkt - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
_param = _input;
|
||||
}
|
||||
// ESP_LOGD(TAG, "Parameter: %s, Pospunkt: %d", _param.c_str(), _pospunkt);
|
||||
std::string _param;
|
||||
int _pospunkt = _input.find_first_of(".");
|
||||
if (_pospunkt > -1)
|
||||
{
|
||||
_param = _input.substr(_pospunkt + 1, _input.length() - _pospunkt - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
_param = _input;
|
||||
}
|
||||
|
||||
return _param;
|
||||
}
|
||||
|
||||
|
||||
bool ClassFlow::getNextLine(FILE* pfile, string *rt)
|
||||
bool ClassFlow::isNewParagraph(std::string input)
|
||||
{
|
||||
char zw[1024];
|
||||
if (pfile == NULL)
|
||||
if ((input[0] == '[') || ((input[0] == ';') && (input[1] == '[')))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ClassFlow::GetNextParagraph(FILE *pFile, std::string &aktparamgraph)
|
||||
{
|
||||
while (getNextLine(pFile, &aktparamgraph) && !isNewParagraph(aktparamgraph));
|
||||
|
||||
if (isNewParagraph(aktparamgraph))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
bool ClassFlow::GetNextParagraph(FILE *pFile, std::string &aktparamgraph, bool &disabled, bool &eof)
|
||||
{
|
||||
while (getNextLine_new(pFile, &aktparamgraph, disabled, eof) && !isNewParagraph(aktparamgraph));
|
||||
|
||||
if (isNewParagraph(aktparamgraph))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
|
||||
bool ClassFlow::getNextLine(FILE *pFile, std::string *rt)
|
||||
{
|
||||
char temp_char[1024];
|
||||
if (pFile == NULL)
|
||||
{
|
||||
*rt = "";
|
||||
return false;
|
||||
}
|
||||
if (!fgets(zw, 1024, pfile))
|
||||
|
||||
if (!fgets(temp_char, 1024, pFile))
|
||||
{
|
||||
*rt = "";
|
||||
ESP_LOGD(TAG, "END OF FILE");
|
||||
return false;
|
||||
}
|
||||
ESP_LOGD(TAG, "%s", zw);
|
||||
*rt = zw;
|
||||
*rt = trim(*rt);
|
||||
while ((zw[0] == ';' || zw[0] == '#' || (rt->size() == 0)) && !(zw[1] == '['))
|
||||
|
||||
ESP_LOGD(TAG, "%s", temp_char);
|
||||
*rt = temp_char;
|
||||
*rt = trim_string_left_right(*rt);
|
||||
|
||||
while ((temp_char[0] == ';' || temp_char[0] == '#' || (rt->size() == 0)) && !(temp_char[1] == '['))
|
||||
{
|
||||
*rt = "";
|
||||
if (!fgets(zw, 1024, pfile))
|
||||
if (!fgets(temp_char, 1024, pFile))
|
||||
{
|
||||
return false;
|
||||
ESP_LOGD(TAG, "%s", zw);
|
||||
*rt = zw;
|
||||
*rt = trim(*rt);
|
||||
}
|
||||
ESP_LOGD(TAG, "%s", temp_char);
|
||||
*rt = temp_char;
|
||||
*rt = trim_string_left_right(*rt);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
bool ClassFlow::getNextLine(FILE *pFile, std::string *rt, bool &disabled, bool &eof)
|
||||
{
|
||||
eof = false;
|
||||
char zw[1024] = "";
|
||||
|
||||
if (pFile == NULL)
|
||||
{
|
||||
*rt = "";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fgets(zw, 1024, pFile))
|
||||
{
|
||||
ESP_LOGD(TAG, "%s", zw);
|
||||
|
||||
if ((strlen(zw) == 0) && feof(pFile))
|
||||
{
|
||||
*rt = "";
|
||||
eof = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*rt = "";
|
||||
eof = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
*rt = zw;
|
||||
*rt = trim_string_left_right(*rt);
|
||||
|
||||
while ((zw[0] == ';' || zw[0] == '#' || (rt->size() == 0)) && !(zw[1] == '['))
|
||||
{
|
||||
fgets(zw, 1024, pFile);
|
||||
ESP_LOGD(TAG, "%s", zw);
|
||||
|
||||
if (feof(pFile))
|
||||
{
|
||||
*rt = "";
|
||||
eof = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
*rt = zw;
|
||||
*rt = trim_string_left_right(*rt);
|
||||
}
|
||||
|
||||
disabled = ((*rt)[0] == ';');
|
||||
return true;
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -21,33 +21,32 @@ struct HTMLInfo
|
||||
std::string filename_org;
|
||||
};
|
||||
|
||||
|
||||
class ClassFlow
|
||||
{
|
||||
protected:
|
||||
bool isNewParagraph(string input);
|
||||
bool GetNextParagraph(FILE* pfile, string& aktparamgraph);
|
||||
bool getNextLine(FILE* pfile, string* rt);
|
||||
|
||||
std::vector<ClassFlow*>* ListFlowControll;
|
||||
std::vector<ClassFlow *> *ListFlowControll;
|
||||
ClassFlow *previousElement;
|
||||
|
||||
virtual void SetInitialParameter(void);
|
||||
|
||||
std::string GetParameterName(std::string _input);
|
||||
|
||||
bool disabled;
|
||||
|
||||
public:
|
||||
ClassFlow(void);
|
||||
ClassFlow(std::vector<ClassFlow*> * lfc);
|
||||
ClassFlow(std::vector<ClassFlow*> * lfc, ClassFlow *_prev);
|
||||
ClassFlow(std::vector<ClassFlow *> *lfc);
|
||||
ClassFlow(std::vector<ClassFlow *> *lfc, ClassFlow *_prev);
|
||||
|
||||
virtual bool ReadParameter(FILE* pfile, string &aktparamgraph);
|
||||
virtual bool doFlow(string time);
|
||||
virtual string getHTMLSingleStep(string host);
|
||||
virtual string name(){return "ClassFlow";};
|
||||
bool isNewParagraph(std::string input);
|
||||
bool GetNextParagraph(FILE *pFile, std::string &aktparamgraph);
|
||||
// bool GetNextParagraph(FILE *pFile, std::string &aktparamgraph, bool &disabled, bool &eof);
|
||||
bool getNextLine(FILE *pFile, std::string *rt);
|
||||
// bool getNextLine(FILE *pFile, std::string *rt, bool &disabled, bool &eof);
|
||||
|
||||
virtual bool ReadParameter(FILE *pFile, std::string &aktparamgraph);
|
||||
virtual bool doFlow(std::string time);
|
||||
virtual std::string getHTMLSingleStep(std::string host);
|
||||
virtual std::string name() { return "ClassFlow"; };
|
||||
};
|
||||
|
||||
#endif //CLASSFLOW_H
|
||||
#endif // CLASSFLOW_H
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
#include "defines.h"
|
||||
|
||||
#include "ClassFlowAlignment.h"
|
||||
#include "ClassControllCamera.h"
|
||||
#include "ClassFlowTakeImage.h"
|
||||
#include "ClassFlow.h"
|
||||
#include "MainFlowControl.h"
|
||||
@@ -8,19 +11,15 @@
|
||||
|
||||
#include "ClassLogFile.h"
|
||||
#include "psram.h"
|
||||
#include "../../include/defines.h"
|
||||
|
||||
static const char *TAG = "ALIGN";
|
||||
|
||||
// #define DEBUG_DETAIL_ON
|
||||
|
||||
void ClassFlowAlignment::SetInitialParameter(void)
|
||||
{
|
||||
initialrotate = 0;
|
||||
anz_ref = 0;
|
||||
use_antialiasing = false;
|
||||
initialflip = false;
|
||||
SaveAllFiles = false;
|
||||
Camera.ImageInitialRotate = 0;
|
||||
Camera.ImageAntialiasing = false;
|
||||
Camera.ImageInitialFlip = false;
|
||||
namerawimage = "/sdcard/img_tmp/raw.jpg";
|
||||
FileStoreRefAlignment = "/sdcard/config/align.txt";
|
||||
ListFlowControll = NULL;
|
||||
@@ -40,126 +39,147 @@ ClassFlowAlignment::ClassFlowAlignment(std::vector<ClassFlow *> *lfc)
|
||||
SetInitialParameter();
|
||||
ListFlowControll = lfc;
|
||||
|
||||
for (int i = 0; i < ListFlowControll->size(); ++i) {
|
||||
if (((*ListFlowControll)[i])->name().compare("ClassFlowTakeImage") == 0) {
|
||||
for (int i = 0; i < ListFlowControll->size(); ++i)
|
||||
{
|
||||
if (((*ListFlowControll)[i])->name().compare("ClassFlowTakeImage") == 0)
|
||||
{
|
||||
ImageBasis = ((ClassFlowTakeImage *)(*ListFlowControll)[i])->rawImage;
|
||||
}
|
||||
}
|
||||
|
||||
// the function take pictures does not exist --> must be created first ONLY FOR TEST PURPOSES
|
||||
if (!ImageBasis) {
|
||||
if (!ImageBasis)
|
||||
{
|
||||
ESP_LOGD(TAG, "CImageBasis had to be created");
|
||||
ImageBasis = new CImageBasis("ImageBasis", namerawimage);
|
||||
}
|
||||
}
|
||||
|
||||
bool ClassFlowAlignment::ReadParameter(FILE *pfile, string &aktparamgraph)
|
||||
bool ClassFlowAlignment::ReadParameter(FILE *pfile, std::string &aktparamgraph)
|
||||
{
|
||||
std::vector<string> splitted;
|
||||
int suchex = 40;
|
||||
int suchey = 40;
|
||||
int alg_algo = 0; // default=0; 1 =HIGHACCURACY; 2= FAST; 3= OFF //add disable aligment algo |01.2023
|
||||
|
||||
aktparamgraph = trim(aktparamgraph);
|
||||
|
||||
aktparamgraph = trim_string_left_right(aktparamgraph);
|
||||
if (aktparamgraph.size() == 0)
|
||||
{
|
||||
if (!this->GetNextParagraph(pfile, aktparamgraph)) {
|
||||
if (!GetNextParagraph(pfile, aktparamgraph))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (aktparamgraph.compare("[Alignment]") != 0)
|
||||
if ((to_upper(aktparamgraph).compare("[ALIGNMENT]") != 0) && (to_upper(aktparamgraph).compare(";[ALIGNMENT]") != 0))
|
||||
{
|
||||
// Paragraph does not fit Alignment
|
||||
return false;
|
||||
}
|
||||
|
||||
while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph))
|
||||
int suchex = 20;
|
||||
int suchey = 20;
|
||||
int maxangle = 45;
|
||||
int alg_algo = 0; // default=0; 1 =HIGHACCURACY; 2= FAST; 3= OFF //add disable aligment algo |01.2023
|
||||
|
||||
std::vector<std::string> splitted;
|
||||
|
||||
while (getNextLine(pfile, &aktparamgraph) && !isNewParagraph(aktparamgraph))
|
||||
{
|
||||
splitted = ZerlegeZeile(aktparamgraph);
|
||||
splitted = split_line(aktparamgraph);
|
||||
|
||||
if ((toUpper(splitted[0]) == "FLIPIMAGESIZE") && (splitted.size() > 1)) {
|
||||
initialflip = alphanumericToBoolean(splitted[1]);
|
||||
}
|
||||
else if (((toUpper(splitted[0]) == "initialrotate") || (toUpper(splitted[0]) == "INITIALROTATE")) && (splitted.size() > 1)) {
|
||||
if (isStringNumeric(splitted[1])) {
|
||||
this->initialrotate = std::stod(splitted[1]);
|
||||
}
|
||||
}
|
||||
else if ((toUpper(splitted[0]) == "SEARCHFIELDX") && (splitted.size() > 1)) {
|
||||
if (isStringNumeric(splitted[1])) {
|
||||
suchex = std::stod(splitted[1]);
|
||||
}
|
||||
}
|
||||
else if ((toUpper(splitted[0]) == "SEARCHFIELDY") && (splitted.size() > 1)) {
|
||||
if (isStringNumeric(splitted[1])) {
|
||||
suchey = std::stod(splitted[1]);
|
||||
}
|
||||
}
|
||||
else if ((toUpper(splitted[0]) == "ANTIALIASING") && (splitted.size() > 1)) {
|
||||
use_antialiasing = alphanumericToBoolean(splitted[1]);
|
||||
}
|
||||
else if ((splitted.size() == 3) && (anz_ref < 2)) {
|
||||
if ((isStringNumeric(splitted[1])) && (isStringNumeric(splitted[2])))
|
||||
{
|
||||
References[anz_ref].image_file = FormatFileName("/sdcard" + splitted[0]);
|
||||
References[anz_ref].target_x = std::stod(splitted[1]);
|
||||
References[anz_ref].target_y = std::stod(splitted[2]);
|
||||
anz_ref++;
|
||||
}
|
||||
else
|
||||
{
|
||||
References[anz_ref].image_file = FormatFileName("/sdcard" + splitted[0]);
|
||||
References[anz_ref].target_x = 10;
|
||||
References[anz_ref].target_y = 10;
|
||||
anz_ref++;
|
||||
}
|
||||
}
|
||||
if (splitted.size() > 1)
|
||||
{
|
||||
std::string _param = to_upper(splitted[0]);
|
||||
|
||||
else if ((toUpper(splitted[0]) == "SAVEALLFILES") && (splitted.size() > 1)) {
|
||||
SaveAllFiles = alphanumericToBoolean(splitted[1]);
|
||||
}
|
||||
else if ((toUpper(splitted[0]) == "ALIGNMENTALGO") && (splitted.size() > 1)) {
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
std::string zw2 = "Alignment mode selected: " + splitted[1];
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, zw2);
|
||||
#endif
|
||||
if (toUpper(splitted[1]) == "HIGHACCURACY") {
|
||||
alg_algo = 1;
|
||||
if (_param == "FLIPIMAGESIZE")
|
||||
{
|
||||
Camera.ImageInitialFlip = alphanumeric_to_boolean(splitted[1]);
|
||||
}
|
||||
if (toUpper(splitted[1]) == "FAST") {
|
||||
alg_algo = 2;
|
||||
else if (_param == "INITIALROTATE")
|
||||
{
|
||||
if (is_string_numeric(splitted[1]))
|
||||
{
|
||||
Camera.ImageInitialRotate = std::stod(splitted[1]);
|
||||
}
|
||||
}
|
||||
if (toUpper(splitted[1]) == "OFF") {
|
||||
// no align algo if set to 3 = off => no draw ref //add disable aligment algo |01.2023
|
||||
alg_algo = 3;
|
||||
else if (_param == "SEARCHFIELDX")
|
||||
{
|
||||
if (is_string_numeric(splitted[1]))
|
||||
{
|
||||
suchex = clip_int(std::stoi(splitted[1]), 320, 0);
|
||||
}
|
||||
}
|
||||
else if (_param == "SEARCHFIELDY")
|
||||
{
|
||||
if (is_string_numeric(splitted[1]))
|
||||
{
|
||||
suchey = clip_int(std::stoi(splitted[1]), 240, 0);
|
||||
}
|
||||
}
|
||||
else if (_param == "SEARCHMAXANGLE")
|
||||
{
|
||||
if (is_string_numeric(splitted[1]))
|
||||
{
|
||||
maxangle = clip_int(std::stoi(splitted[1]), 180, 0);
|
||||
}
|
||||
}
|
||||
else if (_param == "ANTIALIASING")
|
||||
{
|
||||
Camera.ImageAntialiasing = alphanumeric_to_boolean(splitted[1]);
|
||||
}
|
||||
else if (_param == "ALIGNMENTALGO")
|
||||
{
|
||||
if (to_upper(splitted[1]) == "HIGHACCURACY")
|
||||
{
|
||||
alg_algo = 1;
|
||||
}
|
||||
else if (to_upper(splitted[1]) == "FAST")
|
||||
{
|
||||
alg_algo = 2;
|
||||
}
|
||||
else if (to_upper(splitted[1]) == "OFF")
|
||||
{
|
||||
// no align algo if set to 3 = off => no draw ref //add disable aligment algo |01.2023
|
||||
alg_algo = 3;
|
||||
}
|
||||
}
|
||||
else if ((splitted.size() == 3) && (anz_ref < 2))
|
||||
{
|
||||
if (is_string_numeric(splitted[1]) && is_string_numeric(splitted[2]))
|
||||
{
|
||||
References[anz_ref].image_file = format_filename("/sdcard" + splitted[0]);
|
||||
References[anz_ref].target_x = std::stod(splitted[1]);
|
||||
References[anz_ref].target_y = std::stod(splitted[2]);
|
||||
anz_ref++;
|
||||
}
|
||||
else
|
||||
{
|
||||
References[anz_ref].image_file = format_filename("/sdcard" + splitted[0]);
|
||||
References[anz_ref].target_x = 10;
|
||||
References[anz_ref].target_y = 10;
|
||||
anz_ref++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < anz_ref; ++i) {
|
||||
for (int i = 0; i < anz_ref; ++i)
|
||||
{
|
||||
References[i].search_x = suchex;
|
||||
References[i].search_y = suchey;
|
||||
References[i].search_max_angle = (float)maxangle;
|
||||
References[i].fastalg_SAD_criteria = SAD_criteria;
|
||||
References[i].alignment_algo = alg_algo;
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
std::string zw2 = "Alignment mode written: " + std::to_string(alg_algo);
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, zw2);
|
||||
#endif
|
||||
}
|
||||
|
||||
// no align algo if set to 3 = off => no draw ref //add disable aligment algo |01.2023
|
||||
if (References[0].alignment_algo != 3) {
|
||||
if (References[0].alignment_algo != 3)
|
||||
{
|
||||
return LoadReferenceAlignmentValues();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
string ClassFlowAlignment::getHTMLSingleStep(string host)
|
||||
std::string ClassFlowAlignment::getHTMLSingleStep(std::string host)
|
||||
{
|
||||
string result;
|
||||
std::string result;
|
||||
|
||||
result = "<p>Rotated Image: </p> <p><img src=\"" + host + "/img_tmp/rot.jpg\"></p>\n";
|
||||
result = result + "<p>Found Alignment: </p> <p><img src=\"" + host + "/img_tmp/rot_roi.jpg\"></p>\n";
|
||||
@@ -167,29 +187,34 @@ string ClassFlowAlignment::getHTMLSingleStep(string host)
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ClassFlowAlignment::doFlow(string time)
|
||||
bool ClassFlowAlignment::doFlow(std::string time)
|
||||
{
|
||||
#ifdef ALGROI_LOAD_FROM_MEM_AS_JPG
|
||||
// AlgROI needs to be allocated before ImageTMP to avoid heap fragmentation
|
||||
if (!AlgROI) {
|
||||
if (!AlgROI)
|
||||
{
|
||||
AlgROI = (ImageData *)heap_caps_realloc(AlgROI, sizeof(ImageData), MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM);
|
||||
|
||||
if (!AlgROI) {
|
||||
if (!AlgROI)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can't allocate AlgROI");
|
||||
LogFile.WriteHeapInfo("ClassFlowAlignment-doFlow");
|
||||
}
|
||||
}
|
||||
|
||||
if (AlgROI) {
|
||||
if (AlgROI)
|
||||
{
|
||||
ImageBasis->writeToMemoryAsJPG((ImageData *)AlgROI, 90);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!ImageTMP) {
|
||||
ImageTMP = new CImageBasis("tmpImage", ImageBasis); // Make sure the name does not get change, it is relevant for the PSRAM allocation!
|
||||
if (!ImageTMP)
|
||||
{
|
||||
ImageTMP = new CImageBasis("TempImage", ImageBasis); // Make sure the name does not get change, it is relevant for the PSRAM allocation!
|
||||
|
||||
if (!ImageTMP) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can't allocate tmpImage -> Exec this round aborted!");
|
||||
if (!ImageTMP)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can't allocate TempImage -> Exec this round aborted!");
|
||||
LogFile.WriteHeapInfo("ClassFlowAlignment-doFlow");
|
||||
return false;
|
||||
}
|
||||
@@ -198,48 +223,74 @@ bool ClassFlowAlignment::doFlow(string time)
|
||||
delete AlignAndCutImage;
|
||||
AlignAndCutImage = new CAlignAndCutImage("AlignAndCutImage", ImageBasis, ImageTMP);
|
||||
|
||||
if (!AlignAndCutImage) {
|
||||
if (!AlignAndCutImage)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can't allocate AlignAndCutImage -> Exec this round aborted!");
|
||||
LogFile.WriteHeapInfo("ClassFlowAlignment-doFlow");
|
||||
return false;
|
||||
}
|
||||
|
||||
CRotateImage rt("rawImage", AlignAndCutImage, ImageTMP, initialflip);
|
||||
CRotateImage rt("rawImage", AlignAndCutImage, ImageTMP, Camera.ImageInitialFlip);
|
||||
|
||||
if (initialflip) {
|
||||
int _zw = ImageBasis->height;
|
||||
if (Camera.ImageInitialFlip)
|
||||
{
|
||||
int temp_value = ImageBasis->height;
|
||||
ImageBasis->height = ImageBasis->width;
|
||||
ImageBasis->width = _zw;
|
||||
ImageBasis->width = temp_value;
|
||||
|
||||
_zw = ImageTMP->width;
|
||||
temp_value = ImageTMP->width;
|
||||
ImageTMP->width = ImageTMP->height;
|
||||
ImageTMP->height = _zw;
|
||||
ImageTMP->height = temp_value;
|
||||
}
|
||||
|
||||
if ((initialrotate != 0) || initialflip) {
|
||||
if (use_antialiasing) {
|
||||
rt.RotateAntiAliasing(initialrotate);
|
||||
if (((Camera.ImageInitialRotate > 0) && (Camera.ImageInitialRotate < 360)) || Camera.ImageInitialFlip)
|
||||
{
|
||||
if (Camera.ImageAntialiasing)
|
||||
{
|
||||
rt.RotateAntiAliasing(Camera.ImageInitialRotate);
|
||||
}
|
||||
else {
|
||||
rt.Rotate(initialrotate);
|
||||
else
|
||||
{
|
||||
rt.Rotate(Camera.ImageInitialRotate);
|
||||
}
|
||||
|
||||
if (SaveAllFiles) {
|
||||
AlignAndCutImage->SaveToFile(FormatFileName("/sdcard/img_tmp/rot.jpg"));
|
||||
if (Camera.SaveAllFiles)
|
||||
{
|
||||
AlignAndCutImage->SaveToFile(format_filename("/sdcard/img_tmp/rot.jpg"));
|
||||
}
|
||||
}
|
||||
|
||||
// no align algo if set to 3 = off //add disable aligment algo |01.2023
|
||||
if (References[0].alignment_algo != 3) {
|
||||
if (!AlignAndCutImage->Align(&References[0], &References[1])) {
|
||||
SaveReferenceAlignmentValues();
|
||||
if (References[0].alignment_algo != 3)
|
||||
{
|
||||
int res = AlignAndCutImage->Align(&References[0], &References[1]);
|
||||
if (res >= Alignment_OK)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Alignment OK");
|
||||
if (res == Fast_Alignment_OK)
|
||||
{
|
||||
SaveReferenceAlignmentValues();
|
||||
}
|
||||
flowctrl.AlignmentOk = true;
|
||||
}
|
||||
} // no align
|
||||
else
|
||||
{
|
||||
// Alignment failed
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Alignment failed");
|
||||
flowctrl.AlignmentOk = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
flowctrl.AlignmentOk = true;
|
||||
}
|
||||
|
||||
#ifdef ALGROI_LOAD_FROM_MEM_AS_JPG
|
||||
if (AlgROI) {
|
||||
if (AlgROI)
|
||||
{
|
||||
// no align algo if set to 3 = off => no draw ref //add disable aligment algo |01.2023
|
||||
if (References[0].alignment_algo != 3) {
|
||||
if (References[0].alignment_algo != 3)
|
||||
{
|
||||
DrawRef(ImageTMP);
|
||||
}
|
||||
|
||||
@@ -249,31 +300,26 @@ bool ClassFlowAlignment::doFlow(string time)
|
||||
}
|
||||
#endif
|
||||
|
||||
if (SaveAllFiles) {
|
||||
AlignAndCutImage->SaveToFile(FormatFileName("/sdcard/img_tmp/alg.jpg"));
|
||||
ImageTMP->SaveToFile(FormatFileName("/sdcard/img_tmp/alg_roi.jpg"));
|
||||
if (Camera.SaveAllFiles)
|
||||
{
|
||||
AlignAndCutImage->SaveToFile(format_filename("/sdcard/img_tmp/alg.jpg"));
|
||||
ImageTMP->SaveToFile(format_filename("/sdcard/img_tmp/alg_roi.jpg"));
|
||||
}
|
||||
|
||||
// must be deleted to have memory space for loading tflite
|
||||
delete ImageTMP;
|
||||
ImageTMP = NULL;
|
||||
|
||||
// no align algo if set to 3 = off => no draw ref //add disable aligment algo |01.2023
|
||||
if (References[0].alignment_algo != 3) {
|
||||
return LoadReferenceAlignmentValues();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ClassFlowAlignment::SaveReferenceAlignmentValues()
|
||||
void ClassFlowAlignment::SaveReferenceAlignmentValues(void)
|
||||
{
|
||||
FILE *pFile;
|
||||
std::string zwtime, zwvalue;
|
||||
FILE *pFile = fopen(FileStoreRefAlignment.c_str(), "w");
|
||||
|
||||
pFile = fopen(FileStoreRefAlignment.c_str(), "w");
|
||||
|
||||
if (strlen(zwtime.c_str()) == 0) {
|
||||
std::string temp_time;
|
||||
if (strlen(temp_time.c_str()) == 0)
|
||||
{
|
||||
time_t rawtime;
|
||||
struct tm *timeinfo;
|
||||
char buffer[80];
|
||||
@@ -282,22 +328,22 @@ void ClassFlowAlignment::SaveReferenceAlignmentValues()
|
||||
timeinfo = localtime(&rawtime);
|
||||
|
||||
strftime(buffer, 80, "%Y-%m-%dT%H:%M:%S", timeinfo);
|
||||
zwtime = std::string(buffer);
|
||||
temp_time = std::string(buffer);
|
||||
}
|
||||
|
||||
fputs(zwtime.c_str(), pFile);
|
||||
fputs(temp_time.c_str(), pFile);
|
||||
fputs("\n", pFile);
|
||||
|
||||
zwvalue = std::to_string(References[0].fastalg_x) + "\t" + std::to_string(References[0].fastalg_y);
|
||||
zwvalue = zwvalue + "\t" + std::to_string(References[0].fastalg_SAD) + "\t" + std::to_string(References[0].fastalg_min);
|
||||
zwvalue = zwvalue + "\t" + std::to_string(References[0].fastalg_max) + "\t" + std::to_string(References[0].fastalg_avg);
|
||||
fputs(zwvalue.c_str(), pFile);
|
||||
std::string temp_value = std::to_string(References[0].fastalg_x) + "\t" + std::to_string(References[0].fastalg_y);
|
||||
temp_value = temp_value + "\t" + std::to_string(References[0].fastalg_SAD) + "\t" + std::to_string(References[0].fastalg_min);
|
||||
temp_value = temp_value + "\t" + std::to_string(References[0].fastalg_max) + "\t" + std::to_string(References[0].fastalg_avg);
|
||||
fputs(temp_value.c_str(), pFile);
|
||||
fputs("\n", pFile);
|
||||
|
||||
zwvalue = std::to_string(References[1].fastalg_x) + "\t" + std::to_string(References[1].fastalg_y);
|
||||
zwvalue = zwvalue + "\t" + std::to_string(References[1].fastalg_SAD) + "\t" + std::to_string(References[1].fastalg_min);
|
||||
zwvalue = zwvalue + "\t" + std::to_string(References[1].fastalg_max) + "\t" + std::to_string(References[1].fastalg_avg);
|
||||
fputs(zwvalue.c_str(), pFile);
|
||||
temp_value = std::to_string(References[1].fastalg_x) + "\t" + std::to_string(References[1].fastalg_y);
|
||||
temp_value = temp_value + "\t" + std::to_string(References[1].fastalg_SAD) + "\t" + std::to_string(References[1].fastalg_min);
|
||||
temp_value = temp_value + "\t" + std::to_string(References[1].fastalg_max) + "\t" + std::to_string(References[1].fastalg_avg);
|
||||
fputs(temp_value.c_str(), pFile);
|
||||
fputs("\n", pFile);
|
||||
|
||||
fclose(pFile);
|
||||
@@ -305,70 +351,100 @@ void ClassFlowAlignment::SaveReferenceAlignmentValues()
|
||||
|
||||
bool ClassFlowAlignment::LoadReferenceAlignmentValues(void)
|
||||
{
|
||||
FILE *pFile;
|
||||
char zw[1024];
|
||||
string zwvalue;
|
||||
std::vector<string> splitted;
|
||||
|
||||
pFile = fopen(FileStoreRefAlignment.c_str(), "r");
|
||||
|
||||
if (pFile == NULL) {
|
||||
FILE *pFile = fopen(FileStoreRefAlignment.c_str(), "r");
|
||||
if (pFile == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
fgets(zw, 1024, pFile);
|
||||
ESP_LOGD(TAG, "%s", zw);
|
||||
|
||||
fgets(zw, 1024, pFile);
|
||||
splitted = ZerlegeZeile(std::string(zw), " \t");
|
||||
|
||||
if (splitted.size() < 6) {
|
||||
char temp_bufer[1024];
|
||||
// erste Zeile: 2025-03-16T18:50:22
|
||||
if (!fgets(temp_bufer, 1024, pFile))
|
||||
{
|
||||
fclose(pFile);
|
||||
ESP_LOGE(TAG, "/sdcard/config/align.txt empty!");
|
||||
return false;
|
||||
}
|
||||
|
||||
References[0].fastalg_x = stoi(splitted[0]);
|
||||
References[0].fastalg_y = stoi(splitted[1]);
|
||||
References[0].fastalg_SAD = stof(splitted[2]);
|
||||
References[0].fastalg_min = stoi(splitted[3]);
|
||||
References[0].fastalg_max = stoi(splitted[4]);
|
||||
References[0].fastalg_avg = stof(splitted[5]);
|
||||
|
||||
fgets(zw, 1024, pFile);
|
||||
splitted = ZerlegeZeile(std::string(zw));
|
||||
|
||||
if (splitted.size() < 6) {
|
||||
// zweite Zeile: 177 342 -0.000000 6144 1611659784 0.000000
|
||||
if (!fgets(temp_bufer, 1024, pFile))
|
||||
{
|
||||
fclose(pFile);
|
||||
ESP_LOGE(TAG, "/sdcard/config/align.txt empty!");
|
||||
return false;
|
||||
}
|
||||
|
||||
References[1].fastalg_x = stoi(splitted[0]);
|
||||
References[1].fastalg_y = stoi(splitted[1]);
|
||||
References[1].fastalg_SAD = stof(splitted[2]);
|
||||
References[1].fastalg_min = stoi(splitted[3]);
|
||||
References[1].fastalg_max = stoi(splitted[4]);
|
||||
References[1].fastalg_avg = stof(splitted[5]);
|
||||
std::vector<std::string> splitted;
|
||||
splitted = split_line(temp_bufer, "\t");
|
||||
|
||||
if (splitted.size() < 6)
|
||||
{
|
||||
fclose(pFile);
|
||||
ESP_LOGE(TAG, "/sdcard/config/align.txt wrong format!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_string_numeric(splitted[0]) && is_string_numeric(splitted[1]) && is_string_numeric(splitted[2]) &&
|
||||
is_string_numeric(splitted[3]) && is_string_numeric(splitted[4]) && is_string_numeric(splitted[5]))
|
||||
{
|
||||
References[0].fastalg_x = stoi(splitted[0]);
|
||||
References[0].fastalg_y = stoi(splitted[1]);
|
||||
References[0].fastalg_SAD = stof(splitted[2]);
|
||||
References[0].fastalg_min = stoi(splitted[3]);
|
||||
References[0].fastalg_max = stoi(splitted[4]);
|
||||
References[0].fastalg_avg = stof(splitted[5]);
|
||||
}
|
||||
else
|
||||
{
|
||||
fclose(pFile);
|
||||
ESP_LOGE(TAG, "/sdcard/config/align.txt wrong format!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// dritte Zeile: 398 145 -0.000000 6144 1611659784 0.000000
|
||||
if (!fgets(temp_bufer, 1024, pFile))
|
||||
{
|
||||
fclose(pFile);
|
||||
ESP_LOGE(TAG, "/sdcard/config/align.txt empty!");
|
||||
return false;
|
||||
}
|
||||
|
||||
splitted = split_line(temp_bufer, "\t");
|
||||
|
||||
if (splitted.size() < 6)
|
||||
{
|
||||
fclose(pFile);
|
||||
ESP_LOGE(TAG, "/sdcard/config/align.txt wrong format!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_string_numeric(splitted[0]) && is_string_numeric(splitted[1]) && is_string_numeric(splitted[2]) &&
|
||||
is_string_numeric(splitted[3]) && is_string_numeric(splitted[4]) && is_string_numeric(splitted[5]))
|
||||
{
|
||||
References[1].fastalg_x = stoi(splitted[0]);
|
||||
References[1].fastalg_y = stoi(splitted[1]);
|
||||
References[1].fastalg_SAD = stof(splitted[2]);
|
||||
References[1].fastalg_min = stoi(splitted[3]);
|
||||
References[1].fastalg_max = stoi(splitted[4]);
|
||||
References[1].fastalg_avg = stof(splitted[5]);
|
||||
}
|
||||
else
|
||||
{
|
||||
fclose(pFile);
|
||||
ESP_LOGE(TAG, "/sdcard/config/align.txt wrong format!");
|
||||
return false;
|
||||
}
|
||||
|
||||
fclose(pFile);
|
||||
|
||||
/*#ifdef DEBUG_DETAIL_ON
|
||||
std::string _zw = "\tLoadReferences[0]\tx,y:\t" + std::to_string(References[0].fastalg_x) + "\t" + std::to_string(References[0].fastalg_x);
|
||||
_zw = _zw + "\tSAD, min, max, avg:\t" + std::to_string(References[0].fastalg_SAD) + "\t" + std::to_string(References[0].fastalg_min);
|
||||
_zw = _zw + "\t" + std::to_string(References[0].fastalg_max) + "\t" + std::to_string(References[0].fastalg_avg);
|
||||
LogFile.WriteToDedicatedFile("/sdcard/alignment.txt", _zw);
|
||||
_zw = "\tLoadReferences[1]\tx,y:\t" + std::to_string(References[1].fastalg_x) + "\t" + std::to_string(References[1].fastalg_x);
|
||||
_zw = _zw + "\tSAD, min, max, avg:\t" + std::to_string(References[1].fastalg_SAD) + "\t" + std::to_string(References[1].fastalg_min);
|
||||
_zw = _zw + "\t" + std::to_string(References[1].fastalg_max) + "\t" + std::to_string(References[1].fastalg_avg);
|
||||
LogFile.WriteToDedicatedFile("/sdcard/alignment.txt", _zw);
|
||||
#endif*/
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ClassFlowAlignment::DrawRef(CImageBasis *_zw)
|
||||
void ClassFlowAlignment::DrawRef(CImageBasis *Image)
|
||||
{
|
||||
if (_zw->ImageOkay()) {
|
||||
_zw->drawRect(References[0].target_x, References[0].target_y, References[0].width, References[0].height, 255, 0, 0, 2);
|
||||
_zw->drawRect(References[1].target_x, References[1].target_y, References[1].width, References[1].height, 255, 0, 0, 2);
|
||||
if (Image->ImageOkay())
|
||||
{
|
||||
Image->drawRect(References[0].target_x, References[0].target_y, References[0].width, References[0].height, 255, 0, 0, 2);
|
||||
Image->drawRect(References[1].target_x, References[1].target_y, References[1].width, References[1].height, 255, 0, 0, 2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,25 +3,21 @@
|
||||
#ifndef CLASSFLOWALIGNMENT_H
|
||||
#define CLASSFLOWALIGNMENT_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "ClassFlow.h"
|
||||
#include "Helper.h"
|
||||
#include "CAlignAndCutImage.h"
|
||||
#include "CFindTemplate.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
using namespace std;
|
||||
|
||||
class ClassFlowAlignment : public ClassFlow
|
||||
{
|
||||
protected:
|
||||
float initialrotate;
|
||||
bool initialflip;
|
||||
bool use_antialiasing;
|
||||
RefInfo References[2];
|
||||
int anz_ref;
|
||||
string namerawimage;
|
||||
bool SaveAllFiles;
|
||||
std::string namerawimage;
|
||||
CAlignAndCutImage *AlignAndCutImage;
|
||||
std::string FileStoreRefAlignment;
|
||||
float SAD_criteria;
|
||||
@@ -40,12 +36,12 @@ public:
|
||||
|
||||
CAlignAndCutImage *GetAlignAndCutImage() { return AlignAndCutImage; };
|
||||
|
||||
void DrawRef(CImageBasis *_zw);
|
||||
void DrawRef(CImageBasis *Image);
|
||||
|
||||
bool ReadParameter(FILE *pfile, string &aktparamgraph);
|
||||
bool doFlow(string time);
|
||||
string getHTMLSingleStep(string host);
|
||||
string name() { return "ClassFlowAlignment"; };
|
||||
bool ReadParameter(FILE *pfile, std::string &aktparamgraph);
|
||||
bool doFlow(std::string time);
|
||||
std::string getHTMLSingleStep(std::string host);
|
||||
std::string name() { return "ClassFlowAlignment"; };
|
||||
};
|
||||
|
||||
#endif // CLASSFLOWALIGNMENT_H
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,11 +3,11 @@
|
||||
#ifndef CLASSFLOWCNNGENERAL_H
|
||||
#define CLASSFLOWCNNGENERAL_H
|
||||
|
||||
#include"ClassFlowDefineTypes.h"
|
||||
#include "ClassFlowDefineTypes.h"
|
||||
#include "ClassFlowAlignment.h"
|
||||
|
||||
|
||||
enum t_CNNType {
|
||||
enum t_CNNType
|
||||
{
|
||||
AutoDetect,
|
||||
Analogue,
|
||||
Analogue100,
|
||||
@@ -16,64 +16,68 @@ enum t_CNNType {
|
||||
DoubleHyprid10,
|
||||
Digit100,
|
||||
None
|
||||
};
|
||||
};
|
||||
|
||||
class ClassFlowCNNGeneral :
|
||||
public ClassFlowImage
|
||||
class ClassFlowCNNGeneral : public ClassFlowImage
|
||||
{
|
||||
protected:
|
||||
t_CNNType CNNType;
|
||||
std::vector<general*> GENERAL;
|
||||
std::vector<general *> GENERAL;
|
||||
|
||||
float CNNGoodThreshold;
|
||||
|
||||
string cnnmodelfile;
|
||||
int modelxsize, modelysize, modelchannel;
|
||||
std::string cnn_model_file;
|
||||
|
||||
int model_x_size;
|
||||
int model_y_size;
|
||||
int model_channel;
|
||||
|
||||
bool isLogImageSelect;
|
||||
string LogImageSelect;
|
||||
ClassFlowAlignment* flowpostalignment;
|
||||
std::string LogImageSelect;
|
||||
|
||||
bool SaveAllFiles;
|
||||
ClassFlowAlignment *flowpostalignment;
|
||||
|
||||
int PointerEvalAnalogNew(float zahl, int numeral_preceder);
|
||||
int PointerEvalAnalogToDigitNew(float zahl, float numeral_preceder, int eval_predecessors, float AnalogToDigitTransitionStart);
|
||||
int PointerEvalHybridNew(float zahl, float number_of_predecessors, int eval_predecessors, bool Analog_Predecessors = false, float AnalogToDigitTransitionStart=9.2);
|
||||
int PointerEvalAnalog(float number, int numeral_preceder);
|
||||
int PointerEvalAnalogToDigit(float number, float numeral_preceder, int eval_predecessors, float AnalogToDigitTransitionStart);
|
||||
int PointerEvalHybrid(float number, float number_of_predecessors, int eval_predecessors, bool Analog_Predecessors = false, float AnalogToDigitTransitionStart = 9.2);
|
||||
|
||||
bool doNeuralNetwork(std::string time_value);
|
||||
|
||||
|
||||
bool doNeuralNetwork(string time);
|
||||
bool doAlignAndCut(string time);
|
||||
bool doAlignAndCut(std::string time_value);
|
||||
|
||||
bool getNetworkParameter();
|
||||
|
||||
public:
|
||||
ClassFlowCNNGeneral(ClassFlowAlignment *_flowalign, t_CNNType _cnntype = AutoDetect);
|
||||
|
||||
bool ReadParameter(FILE* pfile, string& aktparamgraph);
|
||||
bool doFlow(string time);
|
||||
bool ReadParameter(FILE *pfile, std::string &aktparamgraph);
|
||||
bool doFlow(std::string time_value);
|
||||
|
||||
string getHTMLSingleStep(string host);
|
||||
string getReadout(int _analog, bool _extendedResolution = false, int prev = -1, float _before_narrow_Analog = -1, float AnalogToDigitTransitionStart=9.2);
|
||||
std::string getHTMLSingleStep(std::string host);
|
||||
|
||||
string getReadoutRawString(int _analog);
|
||||
std::vector<double> getMeterValues(int _number);
|
||||
|
||||
void DrawROI(CImageBasis *_zw);
|
||||
std::string getReadout(int _number, bool _extendedResolution = false, int prev = -1, float _before_narrow_Analog = -1, float AnalogToDigitTransitionStart = 9.2);
|
||||
|
||||
std::vector<HTMLInfo*> GetHTMLInfo();
|
||||
std::string getReadoutRawString(int _number);
|
||||
|
||||
int getNumberGENERAL();
|
||||
general* GetGENERAL(int _analog);
|
||||
general* GetGENERAL(string _name, bool _create);
|
||||
general* FindGENERAL(string _name_number);
|
||||
string getNameGENERAL(int _analog);
|
||||
void DrawROI(CImageBasis *Image);
|
||||
|
||||
std::vector<HTMLInfo *> GetHTMLInfo(void);
|
||||
|
||||
int getNumberGENERAL(void);
|
||||
general *GetGENERAL(int _number);
|
||||
general *GetGENERAL(std::string _name, bool _create);
|
||||
general *FindGENERAL(std::string _name_number);
|
||||
std::string getNameGENERAL(int _number);
|
||||
|
||||
bool isExtendedResolution(int _number = 0);
|
||||
|
||||
void UpdateNameNumbers(std::vector<std::string> *_name_numbers);
|
||||
|
||||
t_CNNType getCNNType(){return CNNType;};
|
||||
t_CNNType getCNNType() { return CNNType; };
|
||||
|
||||
string name(){return "ClassFlowCNNGeneral";};
|
||||
std::string name() { return "ClassFlowCNNGeneral"; };
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -10,62 +10,57 @@
|
||||
#include "ClassFlowAlignment.h"
|
||||
#include "ClassFlowCNNGeneral.h"
|
||||
#include "ClassFlowPostProcessing.h"
|
||||
#ifdef ENABLE_MQTT
|
||||
#include "ClassFlowMQTT.h"
|
||||
#endif //ENABLE_MQTT
|
||||
#ifdef ENABLE_INFLUXDB
|
||||
#include "ClassFlowInfluxDB.h"
|
||||
#include "ClassFlowInfluxDBv2.h"
|
||||
#endif //ENABLE_INFLUXDB
|
||||
#ifdef ENABLE_WEBHOOK
|
||||
#include "ClassFlowWebhook.h"
|
||||
#endif //ENABLE_WEBHOOK
|
||||
#include "ClassFlowMQTT.h"
|
||||
#include "ClassFlowInfluxDB.h"
|
||||
#include "ClassFlowInfluxDBv2.h"
|
||||
#include "ClassFlowWebhook.h"
|
||||
#include "ClassFlowCNNGeneral.h"
|
||||
|
||||
class ClassFlowControll :
|
||||
public ClassFlow
|
||||
class ClassFlowControll : public ClassFlow
|
||||
{
|
||||
protected:
|
||||
std::vector<ClassFlow*> FlowControll;
|
||||
ClassFlowPostProcessing* flowpostprocessing;
|
||||
ClassFlowAlignment* flowalignment;
|
||||
ClassFlowCNNGeneral* flowanalog;
|
||||
ClassFlowCNNGeneral* flowdigit;
|
||||
// ClassFlowDigit* flowdigit;
|
||||
ClassFlowTakeImage* flowtakeimage;
|
||||
ClassFlow* CreateClassFlow(std::string _type);
|
||||
std::vector<ClassFlow *> FlowControll;
|
||||
ClassFlowPostProcessing *flowpostprocessing;
|
||||
ClassFlowAlignment *flowalignment;
|
||||
ClassFlowCNNGeneral *flowanalog;
|
||||
ClassFlowCNNGeneral *flowdigit;
|
||||
// ClassFlowDigit* flowdigit;
|
||||
ClassFlowTakeImage *flowtakeimage;
|
||||
ClassFlow *CreateClassFlow(std::string _type);
|
||||
|
||||
bool AutoStart;
|
||||
float AutoInterval;
|
||||
void SetInitialParameter(void);
|
||||
std::string aktstatusWithTime;
|
||||
std::string aktstatus;
|
||||
int aktRunNr;
|
||||
|
||||
public:
|
||||
bool SetupModeActive;
|
||||
bool AutoStart = false;
|
||||
float AutoInterval = 5;
|
||||
|
||||
bool SetupModeActive = false;
|
||||
bool AlignmentOk = false;
|
||||
|
||||
void InitFlow(std::string config);
|
||||
bool doFlow(string time);
|
||||
void doFlowTakeImageOnly(string time);
|
||||
bool getStatusSetupModus(){return SetupModeActive;};
|
||||
string getReadout(bool _rawvalue, bool _noerror, int _number);
|
||||
string getReadoutAll(int _type);
|
||||
bool doFlow(std::string time);
|
||||
void doFlowTakeImageOnly(std::string time);
|
||||
bool getStatusSetupModus() { return SetupModeActive; };
|
||||
std::string getReadout(bool _rawvalue, bool _noerror, int _number);
|
||||
std::string getReadoutAll(int _type);
|
||||
bool UpdatePrevalue(std::string _newvalue, std::string _numbers, bool _extern);
|
||||
string GetPrevalue(std::string _number = "");
|
||||
bool ReadParameter(FILE* pfile, string& aktparamgraph);
|
||||
string getJSON();
|
||||
const std::vector<NumberPost*> &getNumbers();
|
||||
string getNumbersName();
|
||||
std::string GetPrevalue(std::string _number = "");
|
||||
bool ReadParameter(FILE *pFile, string &aktparamgraph);
|
||||
std::string getJSON();
|
||||
const std::vector<NumberPost *> &getNumbers();
|
||||
std::string getNumbersName();
|
||||
|
||||
string TranslateAktstatus(std::string _input);
|
||||
std::string TranslateAktstatus(std::string _input);
|
||||
|
||||
#ifdef ALGROI_LOAD_FROM_MEM_AS_JPG
|
||||
void DigitDrawROI(CImageBasis *_zw);
|
||||
void AnalogDrawROI(CImageBasis *_zw);
|
||||
#endif
|
||||
#ifdef ALGROI_LOAD_FROM_MEM_AS_JPG
|
||||
void DigitDrawROI(CImageBasis *TempImage);
|
||||
void AnalogDrawROI(CImageBasis *TempImage);
|
||||
#endif
|
||||
|
||||
esp_err_t GetJPGStream(std::string _fn, httpd_req_t *req);
|
||||
esp_err_t GetJPGStream(std::string file_name, httpd_req_t *req);
|
||||
esp_err_t SendRawJPG(httpd_req_t *req);
|
||||
|
||||
std::string doSingleStep(std::string _stepname, std::string _host);
|
||||
@@ -73,26 +68,21 @@ public:
|
||||
bool getIsAutoStart();
|
||||
void setAutoStartInterval(long &_interval);
|
||||
|
||||
std::string* getActStatusWithTime();
|
||||
std::string* getActStatus();
|
||||
std::string *getActStatusWithTime();
|
||||
std::string *getActStatus();
|
||||
void setActStatus(std::string _aktstatus);
|
||||
|
||||
std::vector<HTMLInfo*> GetAllDigit();
|
||||
std::vector<HTMLInfo*> GetAllAnalog();
|
||||
std::vector<HTMLInfo *> GetAllDigit();
|
||||
std::vector<HTMLInfo *> GetAllAnalog();
|
||||
|
||||
t_CNNType GetTypeDigit();
|
||||
t_CNNType GetTypeAnalog();
|
||||
|
||||
#ifdef ENABLE_MQTT
|
||||
bool StartMQTTService();
|
||||
#endif //ENABLE_MQTT
|
||||
|
||||
int CleanTempFolder();
|
||||
|
||||
string name(){return "ClassFlowControll";};
|
||||
std::string name() { return "ClassFlowControll"; };
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -5,81 +5,81 @@
|
||||
|
||||
#include "ClassFlowImage.h"
|
||||
|
||||
/**
|
||||
* Properties of one ROI
|
||||
* FIXME: naming of members could use some refactoring to comply with common C++ coding style guidelines
|
||||
*/
|
||||
struct roi {
|
||||
int posx, posy, deltax, deltay;
|
||||
struct roi
|
||||
{
|
||||
int pos_x;
|
||||
int pos_y;
|
||||
int delta_x;
|
||||
int delta_y;
|
||||
float raw_result_float;
|
||||
int raw_result_klasse;
|
||||
float result_float;
|
||||
int result_klasse;
|
||||
bool isReject, CCW;
|
||||
bool isReject;
|
||||
bool ccw;
|
||||
string name;
|
||||
CImageBasis *image, *image_org;
|
||||
CImageBasis *image;
|
||||
CImageBasis *image_org;
|
||||
};
|
||||
|
||||
/**
|
||||
* FIXME: Why is this additional layer needed?
|
||||
*/
|
||||
struct general {
|
||||
struct general
|
||||
{
|
||||
string name;
|
||||
std::vector<roi*> ROI;
|
||||
std::vector<roi *> ROI;
|
||||
};
|
||||
|
||||
enum t_RateType {
|
||||
enum t_RateType
|
||||
{
|
||||
AbsoluteChange, // ignores the time difference; only the value difference is used comparison with NumberPost.maxRate
|
||||
RateChange // time difference is considered and a normalized rate is used for comparison with NumberPost.maxRate
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Holds all properties and settings of a sequence. A sequence is a set of digit and/or analog ROIs that are combined to
|
||||
* provide one meter reading (value).
|
||||
* FIXME: can be renamed to `Sequence`
|
||||
*/
|
||||
struct NumberPost {
|
||||
float MaxRateValue; // maxRate; upper bound for the difference between two consecutive readings; affected by maxRateType;
|
||||
bool useMaxRateValue; // consistencyChecksEnabled; enables consistency checks; uses maxRate and maxRateType
|
||||
t_RateType MaxRateType; // maxRateType; affects how the value of maxRate is used for comparing the current and previous value
|
||||
bool ErrorMessage; // FIXME: not used; can be removed
|
||||
int ChangeRateThreshold; // threshold parameter for negative rate detection
|
||||
bool PreValueOkay; // previousValueValid; indicates that the reading of the previous round has no errors
|
||||
bool AllowNegativeRates; // allowNegativeRate; defines if the consistency checks allow negative rates between consecutive meter readings.
|
||||
bool IgnoreLeadingNaN;
|
||||
bool checkDigitIncreaseConsistency; // extendedConsistencyCheck; performs an additional consistency check to avoid wrong readings
|
||||
time_t timeStampLastValue; // Timestamp for the last read value; is used for the log
|
||||
time_t timeStampLastPreValue; // Timestamp for the last PreValue set; is used for useMaxRateValue
|
||||
time_t timeStampTimeUTC; // FIXME: not used; can be removed.
|
||||
string timeStamp; // localTimeStr; timestamp of last valid reading formatted as local time
|
||||
double FlowRateAct; // currentRate; ΔValue/min; since usage is not limited to water meters, the physical unit is not known.
|
||||
double PreValue; // lastValidValue; most recent value that could be read w/o any errors
|
||||
double Value; // value; most recent readout; may include corrections
|
||||
string ReturnRateValue; // currentRateStr; current normalized rate; ΔValue/min
|
||||
string ReturnChangeAbsolute; // currentChangeStr; absolute difference between current and previous measurement
|
||||
string ReturnRawValue; // rawValueStr; Raw value (with N & leading 0)
|
||||
string ReturnValue; // valueStr; corrected return value, if necessary with error message
|
||||
string ReturnPreValue; // lastValidValueStr; corrected return value without error message
|
||||
string ErrorMessageText; // errorMessage; Error message for consistency checks
|
||||
int AnzahlAnalog; // numAnalogRoi; number of analog ROIs used in this sequence
|
||||
int AnzahlDigit; // numDigitRoi; number of digit ROIs used in this sequence
|
||||
int DecimalShift; // decimalShift; each increment shifts the decimal separator by one digit; value=value*10^decimalShift; pos. value shifts to the right
|
||||
int DecimalShiftInitial; // decimalShiftInitial; same as decimalShift but is a const to reset decimalShift after calculations
|
||||
struct NumberPost
|
||||
{
|
||||
float MaxFlowRate;
|
||||
bool useMaxFlowRate;
|
||||
float MaxRateValue; // maxRate; upper bound for the difference between two consecutive readings; affected by maxRateType;
|
||||
bool useMaxRateValue; // consistencyChecksEnabled; enables consistency checks; uses maxRate and maxRateType
|
||||
t_RateType MaxRateType; // maxRateType; affects how the value of maxRate is used for comparing the current and previous value
|
||||
bool ErrorMessage; // FIXME: not used; can be removed
|
||||
int ChangeRateThreshold; // threshold parameter for negative rate detection
|
||||
bool PreValueOkay; // previousValueValid; indicates that the reading of the previous round has no errors
|
||||
bool AllowNegativeRates; // allowNegativeRate; defines if the consistency checks allow negative rates between consecutive meter readings.
|
||||
bool IgnoreLeadingNaN; //
|
||||
time_t timeStampLastValue; // Timestamp for the last read value; is used for the log
|
||||
time_t timeStampLastPreValue; // Timestamp for the last PreValue set; is used for useMaxRateValue
|
||||
time_t timeStampTimeUTC; //
|
||||
string timeStamp; // localTimeStr; timestamp of last valid reading formatted as local time
|
||||
double FlowRateAct; // currentRate; ΔValue/min; since usage is not limited to water meters, the physical unit is not known.
|
||||
double PreValue; // lastValidValue; most recent value that could be read w/o any errors
|
||||
double Value; // value; most recent readout; may include corrections
|
||||
string ReturnRateValue; // currentRateStr; current normalized rate; ΔValue/min
|
||||
string ReturnChangeAbsolute; // currentChangeStr; absolute difference between current and previous measurement
|
||||
string ReturnRawValue; // rawValueStr; Raw value (with N & leading 0)
|
||||
string ReturnValue; // valueStr; corrected return value, if necessary with error message
|
||||
string ReturnPreValue; // lastValidValueStr; corrected return value without error message
|
||||
string ErrorMessageText; // errorMessage; Error message for consistency checks
|
||||
int AnzahlAnalog; // numAnalogRoi; number of analog ROIs used in this sequence
|
||||
int AnzahlDigit; // numDigitRoi; number of digit ROIs used in this sequence
|
||||
int DecimalShift; // decimalShift; each increment shifts the decimal separator by one digit; value=value*10^decimalShift; pos. value shifts to the right
|
||||
int DecimalShiftInitial; // decimalShiftInitial; same as decimalShift but is a const to reset decimalShift after calculations
|
||||
float AnalogToDigitTransitionStart; // AnalogToDigitTransitionStartValue; FIXME: need a better description; When is the digit > x.1, i.e. when does it start to tilt?
|
||||
int Nachkomma; // decimalPlaces; usually defined by the number of analog ROIs; affected by DecimalShift
|
||||
int Nachkomma; // decimalPlaces; usually defined by the number of analog ROIs; affected by DecimalShift
|
||||
|
||||
string DomoticzIdx; // Domoticz counter Idx
|
||||
string DomoticzIdx; // Domoticz counter Idx
|
||||
|
||||
string FieldV1; // influxdbFieldName_v1; Name of the Field in InfluxDBv1
|
||||
string MeasurementV1; // influxdbMeasurementName_v1; Name of the Measurement in InfluxDBv1
|
||||
string FieldV1; // influxdbFieldName_v1; Name of the Field in InfluxDBv1
|
||||
string MeasurementV1; // influxdbMeasurementName_v1; Name of the Measurement in InfluxDBv1
|
||||
|
||||
string FieldV2; // influxdbFieldName_v2; Name of the Field in InfluxDBv2
|
||||
string MeasurementV2; // influxdbMeasurementName_v2; Name of the Measurement in InfluxDBv2
|
||||
string FieldV2; // influxdbFieldName_v2; Name of the Field in InfluxDBv2
|
||||
string MeasurementV2; // influxdbMeasurementName_v2; Name of the Measurement in InfluxDBv2
|
||||
|
||||
bool isExtendedResolution; // extendResolution; Adds the decimal place of the least significant analog ROI to the value
|
||||
bool isExtendedResolution; // extendResolution; Adds the decimal place of the least significant analog ROI to the value
|
||||
|
||||
general *digit_roi; // digitRoi; set of digit ROIs for the sequence
|
||||
general *analog_roi; // analogRoi; set of analog ROIs for the sequence
|
||||
general *digit_roi; // digitRoi; set of digit ROIs for the sequence
|
||||
general *analog_roi; // analogRoi; set of analog ROIs for the sequence
|
||||
|
||||
string name; // name; Designation for the sequence
|
||||
string name; // name; Designation for the sequence
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,71 +1,66 @@
|
||||
#include "defines.h"
|
||||
|
||||
#include "ClassFlowImage.h"
|
||||
#include <string>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#include <dirent.h>
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#include "time_sntp.h"
|
||||
#include "ClassLogFile.h"
|
||||
#include "CImageBasis.h"
|
||||
#include "esp_log.h"
|
||||
#include "../../include/defines.h"
|
||||
|
||||
static const char* TAG = "FLOWIMAGE";
|
||||
static const char *TAG = "FLOWIMAGE";
|
||||
|
||||
|
||||
ClassFlowImage::ClassFlowImage(const char* logTag)
|
||||
ClassFlowImage::ClassFlowImage(const char *logTag)
|
||||
{
|
||||
this->logTag = logTag;
|
||||
isLogImage = false;
|
||||
this->logTag = logTag;
|
||||
isLogImage = false;
|
||||
disabled = false;
|
||||
this->imagesRetention = 5;
|
||||
}
|
||||
|
||||
ClassFlowImage::ClassFlowImage(std::vector<ClassFlow*> * lfc, const char* logTag) : ClassFlow(lfc)
|
||||
ClassFlowImage::ClassFlowImage(std::vector<ClassFlow *> *lfc, const char *logTag) : ClassFlow(lfc)
|
||||
{
|
||||
this->logTag = logTag;
|
||||
isLogImage = false;
|
||||
this->logTag = logTag;
|
||||
isLogImage = false;
|
||||
disabled = false;
|
||||
this->imagesRetention = 5;
|
||||
}
|
||||
|
||||
ClassFlowImage::ClassFlowImage(std::vector<ClassFlow*> * lfc, ClassFlow *_prev, const char* logTag) : ClassFlow(lfc, _prev)
|
||||
ClassFlowImage::ClassFlowImage(std::vector<ClassFlow *> *lfc, ClassFlow *_prev, const char *logTag) : ClassFlow(lfc, _prev)
|
||||
{
|
||||
this->logTag = logTag;
|
||||
isLogImage = false;
|
||||
this->logTag = logTag;
|
||||
isLogImage = false;
|
||||
disabled = false;
|
||||
this->imagesRetention = 5;
|
||||
}
|
||||
|
||||
string ClassFlowImage::CreateLogFolder(string time)
|
||||
{
|
||||
if (!isLogImage)
|
||||
return "";
|
||||
|
||||
string ClassFlowImage::CreateLogFolder(string time) {
|
||||
if (!isLogImage)
|
||||
return "";
|
||||
|
||||
string logPath = imagesLocation + "/" + time.LOGFILE_TIME_FORMAT_DATE_EXTR + "/" + time.LOGFILE_TIME_FORMAT_HOUR_EXTR;
|
||||
string logPath = imagesLocation + "/" + time.LOGFILE_TIME_FORMAT_DATE_EXTR + "/" + time.LOGFILE_TIME_FORMAT_HOUR_EXTR;
|
||||
isLogImage = mkdir_r(logPath.c_str(), S_IRWXU) == 0;
|
||||
if (!isLogImage) {
|
||||
if (!isLogImage)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Can't create log folder for analog images. Path " + logPath);
|
||||
}
|
||||
|
||||
return logPath;
|
||||
return logPath;
|
||||
}
|
||||
|
||||
void ClassFlowImage::LogImage(string logPath, string name, float *resultFloat, int *resultInt, string time, CImageBasis *_img) {
|
||||
if (!isLogImage)
|
||||
return;
|
||||
void ClassFlowImage::LogImage(string logPath, string name, float *resultFloat, int *resultInt, string time, CImageBasis *_img)
|
||||
{
|
||||
if (!isLogImage)
|
||||
return;
|
||||
|
||||
char buf[10];
|
||||
|
||||
char buf[10];
|
||||
|
||||
if (resultFloat != NULL) {
|
||||
if (resultFloat != NULL)
|
||||
{
|
||||
if (*resultFloat < 0)
|
||||
sprintf(buf, "N.N_");
|
||||
else
|
||||
@@ -74,47 +69,51 @@ void ClassFlowImage::LogImage(string logPath, string name, float *resultFloat, i
|
||||
if (strcmp(buf, "10.0_") == 0)
|
||||
sprintf(buf, "0.0_");
|
||||
}
|
||||
}
|
||||
else if (resultInt != NULL)
|
||||
{
|
||||
sprintf(buf, "%d_", *resultInt);
|
||||
}
|
||||
else
|
||||
{
|
||||
buf[0] = '\0';
|
||||
}
|
||||
|
||||
} else if (resultInt != NULL) {
|
||||
sprintf(buf, "%d_", *resultInt);
|
||||
} else {
|
||||
buf[0] = '\0';
|
||||
}
|
||||
|
||||
string nm = logPath + "/" + buf + name + "_" + time + ".jpg";
|
||||
nm = FormatFileName(nm);
|
||||
string output = "/sdcard/img_tmp/" + name + ".jpg";
|
||||
output = FormatFileName(output);
|
||||
ESP_LOGD(logTag, "save to file: %s", nm.c_str());
|
||||
_img->SaveToFile(nm);
|
||||
// CopyFile(output, nm);
|
||||
string nm = logPath + "/" + buf + name + "_" + time + ".jpg";
|
||||
nm = format_filename(nm);
|
||||
string output = "/sdcard/img_tmp/" + name + ".jpg";
|
||||
output = format_filename(output);
|
||||
ESP_LOGD(logTag, "save to file: %s", nm.c_str());
|
||||
_img->SaveToFile(nm);
|
||||
}
|
||||
|
||||
void ClassFlowImage::RemoveOldLogs()
|
||||
{
|
||||
if (!isLogImage)
|
||||
return;
|
||||
if (!isLogImage)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "remove old images");
|
||||
if (imagesRetention == 0) {
|
||||
ESP_LOGD(TAG, "remove old images");
|
||||
if (imagesRetention == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
time_t rawtime;
|
||||
struct tm* timeinfo;
|
||||
struct tm *timeinfo;
|
||||
char cmpfilename[30];
|
||||
|
||||
time(&rawtime);
|
||||
rawtime = addDays(rawtime, -1 * imagesRetention + 1);
|
||||
rawtime = add_days(rawtime, -1 * imagesRetention + 1);
|
||||
timeinfo = localtime(&rawtime);
|
||||
//ESP_LOGD(TAG, "ImagefileRetentionInDays: %d", imagesRetention);
|
||||
|
||||
strftime(cmpfilename, 30, LOGFILE_TIME_FORMAT, timeinfo);
|
||||
//ESP_LOGD(TAG, "file name to compare: %s", cmpfilename);
|
||||
string folderName = string(cmpfilename).LOGFILE_TIME_FORMAT_DATE_EXTR;
|
||||
string folderName = string(cmpfilename).LOGFILE_TIME_FORMAT_DATE_EXTR;
|
||||
|
||||
DIR *dir = opendir(imagesLocation.c_str());
|
||||
if (!dir) {
|
||||
if (!dir)
|
||||
{
|
||||
ESP_LOGE(TAG, "Failed to stat dir: %s", imagesLocation.c_str());
|
||||
return;
|
||||
}
|
||||
@@ -122,19 +121,22 @@ void ClassFlowImage::RemoveOldLogs()
|
||||
struct dirent *entry;
|
||||
int deleted = 0;
|
||||
int notDeleted = 0;
|
||||
while ((entry = readdir(dir)) != NULL) {
|
||||
while ((entry = readdir(dir)) != NULL)
|
||||
{
|
||||
string folderPath = imagesLocation + "/" + entry->d_name;
|
||||
if (entry->d_type == DT_DIR) {
|
||||
//ESP_LOGD(TAG, "Compare %s to %s", entry->d_name, folderName.c_str());
|
||||
if ((strlen(entry->d_name) == folderName.length()) && (strcmp(entry->d_name, folderName.c_str()) < 0)) {
|
||||
removeFolder(folderPath.c_str(), logTag);
|
||||
if (entry->d_type == DT_DIR)
|
||||
{
|
||||
if ((strlen(entry->d_name) == folderName.length()) && (strcmp(entry->d_name, folderName.c_str()) < 0))
|
||||
{
|
||||
remove_folder(folderPath.c_str(), logTag);
|
||||
deleted++;
|
||||
} else {
|
||||
notDeleted ++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
notDeleted++;
|
||||
}
|
||||
}
|
||||
}
|
||||
ESP_LOGD(TAG, "Image folder deleted: %d | Image folder not deleted: %d", deleted, notDeleted);
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,20 +11,19 @@ class ClassFlowImage : public ClassFlow
|
||||
{
|
||||
protected:
|
||||
string imagesLocation;
|
||||
bool isLogImage;
|
||||
unsigned short imagesRetention;
|
||||
const char* logTag;
|
||||
bool isLogImage;
|
||||
unsigned short imagesRetention;
|
||||
const char *logTag;
|
||||
|
||||
string CreateLogFolder(string time);
|
||||
void LogImage(string logPath, string name, float *resultFloat, int *resultInt, string time, CImageBasis *_img);
|
||||
|
||||
|
||||
public:
|
||||
ClassFlowImage(const char* logTag);
|
||||
ClassFlowImage(std::vector<ClassFlow*> * lfc, const char* logTag);
|
||||
ClassFlowImage(std::vector<ClassFlow*> * lfc, ClassFlow *_prev, const char* logTag);
|
||||
ClassFlowImage(const char *logTag);
|
||||
ClassFlowImage(std::vector<ClassFlow *> *lfc, const char *logTag);
|
||||
ClassFlowImage(std::vector<ClassFlow *> *lfc, ClassFlow *_prev, const char *logTag);
|
||||
|
||||
void RemoveOldLogs();
|
||||
};
|
||||
|
||||
#endif //CLASSFLOWIMAGE_H
|
||||
#endif // CLASSFLOWIMAGE_H
|
||||
|
||||
@@ -1,35 +1,38 @@
|
||||
#ifdef ENABLE_INFLUXDB
|
||||
#include "defines.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <time.h>
|
||||
|
||||
#include "ClassFlowInfluxDB.h"
|
||||
|
||||
#include "Helper.h"
|
||||
#include "connect_wlan.h"
|
||||
#include "connect_wifi_sta.h"
|
||||
|
||||
#include "time_sntp.h"
|
||||
#include "interface_influxdb.h"
|
||||
|
||||
#include "ClassFlowPostProcessing.h"
|
||||
#include "esp_log.h"
|
||||
#include "../../include/defines.h"
|
||||
|
||||
#include "ClassLogFile.h"
|
||||
|
||||
#include <time.h>
|
||||
static const char *TAG = "INFLUXDB";
|
||||
|
||||
static const char* TAG = "INFLUXDB";
|
||||
influxDBv1_controll_config_t influxDBv1_controll_config;
|
||||
|
||||
void ClassFlowInfluxDB::SetInitialParameter(void)
|
||||
{
|
||||
uri = "";
|
||||
database = "";
|
||||
influxDBv1_controll_config.enabled = false;
|
||||
influxDBv1_controll_config.uri = "";
|
||||
influxDBv1_controll_config.database = "";
|
||||
influxDBv1_controll_config.user = "";
|
||||
influxDBv1_controll_config.password = "";
|
||||
influxDBv1_controll_config.oldValue = "";
|
||||
|
||||
OldValue = "";
|
||||
flowpostprocessing = NULL;
|
||||
user = "";
|
||||
password = "";
|
||||
previousElement = NULL;
|
||||
ListFlowControll = NULL;
|
||||
|
||||
disabled = false;
|
||||
InfluxDBenable = false;
|
||||
}
|
||||
|
||||
ClassFlowInfluxDB::ClassFlowInfluxDB()
|
||||
@@ -37,24 +40,23 @@ ClassFlowInfluxDB::ClassFlowInfluxDB()
|
||||
SetInitialParameter();
|
||||
}
|
||||
|
||||
ClassFlowInfluxDB::ClassFlowInfluxDB(std::vector<ClassFlow*>* lfc)
|
||||
ClassFlowInfluxDB::ClassFlowInfluxDB(std::vector<ClassFlow *> *lfc)
|
||||
{
|
||||
SetInitialParameter();
|
||||
|
||||
ListFlowControll = lfc;
|
||||
|
||||
for (int i = 0; i < ListFlowControll->size(); ++i)
|
||||
{
|
||||
if (((*ListFlowControll)[i])->name().compare("ClassFlowPostProcessing") == 0)
|
||||
{
|
||||
flowpostprocessing = (ClassFlowPostProcessing*) (*ListFlowControll)[i];
|
||||
flowpostprocessing = (ClassFlowPostProcessing *)(*ListFlowControll)[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ClassFlowInfluxDB::ClassFlowInfluxDB(std::vector<ClassFlow*>* lfc, ClassFlow *_prev)
|
||||
ClassFlowInfluxDB::ClassFlowInfluxDB(std::vector<ClassFlow *> *lfc, ClassFlow *_prev)
|
||||
{
|
||||
SetInitialParameter();
|
||||
|
||||
previousElement = _prev;
|
||||
ListFlowControll = lfc;
|
||||
|
||||
@@ -62,176 +64,182 @@ ClassFlowInfluxDB::ClassFlowInfluxDB(std::vector<ClassFlow*>* lfc, ClassFlow *_p
|
||||
{
|
||||
if (((*ListFlowControll)[i])->name().compare("ClassFlowPostProcessing") == 0)
|
||||
{
|
||||
flowpostprocessing = (ClassFlowPostProcessing*) (*ListFlowControll)[i];
|
||||
flowpostprocessing = (ClassFlowPostProcessing *)(*ListFlowControll)[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool ClassFlowInfluxDB::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
bool ClassFlowInfluxDB::ReadParameter(FILE *pFile, std::string &aktparamgraph)
|
||||
{
|
||||
std::vector<string> splitted;
|
||||
|
||||
aktparamgraph = trim(aktparamgraph);
|
||||
|
||||
aktparamgraph = trim_string_left_right(aktparamgraph);
|
||||
if (aktparamgraph.size() == 0)
|
||||
if (!this->GetNextParagraph(pfile, aktparamgraph))
|
||||
return false;
|
||||
|
||||
if (toUpper(aktparamgraph).compare("[INFLUXDB]") != 0)
|
||||
return false;
|
||||
|
||||
while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph))
|
||||
{
|
||||
ESP_LOGD(TAG, "while loop reading line: %s", aktparamgraph.c_str());
|
||||
splitted = ZerlegeZeile(aktparamgraph);
|
||||
std::string _param = GetParameterName(splitted[0]);
|
||||
|
||||
if ((toUpper(_param) == "USER") && (splitted.size() > 1))
|
||||
if (!GetNextParagraph(pFile, aktparamgraph))
|
||||
{
|
||||
this->user = splitted[1];
|
||||
}
|
||||
if ((toUpper(_param) == "PASSWORD") && (splitted.size() > 1))
|
||||
{
|
||||
this->password = splitted[1];
|
||||
}
|
||||
if ((toUpper(_param) == "URI") && (splitted.size() > 1))
|
||||
{
|
||||
this->uri = splitted[1];
|
||||
}
|
||||
if (((toUpper(_param) == "DATABASE")) && (splitted.size() > 1))
|
||||
{
|
||||
this->database = splitted[1];
|
||||
}
|
||||
if (((toUpper(_param) == "MEASUREMENT")) && (splitted.size() > 1))
|
||||
{
|
||||
handleMeasurement(splitted[0], splitted[1]);
|
||||
}
|
||||
if (((toUpper(_param) == "FIELD")) && (splitted.size() > 1))
|
||||
{
|
||||
handleFieldname(splitted[0], splitted[1]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ((uri.length() > 0) && (database.length() > 0))
|
||||
if ((to_upper(aktparamgraph).compare("[INFLUXDB]") != 0) && (to_upper(aktparamgraph).compare(";[INFLUXDB]") != 0))
|
||||
{
|
||||
// ESP_LOGD(TAG, "Init InfluxDB with uri: %s, measurement: %s, user: %s, password: %s", uri.c_str(), measurement.c_str(), user.c_str(), password.c_str());
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Init InfluxDB with uri: " + uri + ", user: " + user + ", password: " + password);
|
||||
return false;
|
||||
}
|
||||
|
||||
/////////////////////// NEW //////////////////////////
|
||||
// InfluxDBInit(uri, database, user, password);
|
||||
influxDB.InfluxDBInitV1(uri, database, user, password);
|
||||
/////////////////////// NEW //////////////////////////
|
||||
if (aktparamgraph[0] == ';')
|
||||
{
|
||||
influxDBv1_controll_config.enabled = false;
|
||||
while (getNextLine(pFile, &aktparamgraph) && !isNewParagraph(aktparamgraph));
|
||||
ESP_LOGD(TAG, "InfluxDBv1 is disabled!");
|
||||
|
||||
InfluxDBenable = true;
|
||||
} else {
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "InfluxDB init skipped as we are missing some parameters");
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<std::string> splitted;
|
||||
|
||||
while (getNextLine(pFile, &aktparamgraph) && !isNewParagraph(aktparamgraph))
|
||||
{
|
||||
splitted = split_line(aktparamgraph);
|
||||
|
||||
if (splitted.size() > 1)
|
||||
{
|
||||
std::string _param = to_upper(GetParameterName(splitted[0]));
|
||||
|
||||
if (_param == "USER")
|
||||
{
|
||||
influxDBv1_controll_config.user = splitted[1];
|
||||
}
|
||||
else if (_param == "PASSWORD")
|
||||
{
|
||||
influxDBv1_controll_config.password = splitted[1];
|
||||
}
|
||||
else if (_param == "URI")
|
||||
{
|
||||
influxDBv1_controll_config.uri = splitted[1];
|
||||
}
|
||||
else if (_param == "DATABASE")
|
||||
{
|
||||
influxDBv1_controll_config.database = splitted[1];
|
||||
}
|
||||
else if (_param == "MEASUREMENT")
|
||||
{
|
||||
handleMeasurement(splitted[0], splitted[1]);
|
||||
}
|
||||
else if (_param == "FIELD")
|
||||
{
|
||||
handleFieldname(splitted[0], splitted[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((influxDBv1_controll_config.uri.length() > 0) && (influxDBv1_controll_config.database.length() > 0))
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Init InfluxDBv1 with uri: " + influxDBv1_controll_config.uri + ", user: " + influxDBv1_controll_config.user + ", password: " + influxDBv1_controll_config.password);
|
||||
influxDB.InfluxDBInitV1(influxDBv1_controll_config.uri, influxDBv1_controll_config.database, influxDBv1_controll_config.user, influxDBv1_controll_config.password);
|
||||
influxDBv1_controll_config.enabled = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "InfluxDBv1 init skipped as we are missing some parameters");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ClassFlowInfluxDB::doFlow(string zwtime)
|
||||
bool ClassFlowInfluxDB::doFlow(std::string temp_time)
|
||||
{
|
||||
if (!InfluxDBenable)
|
||||
if (!influxDBv1_controll_config.enabled)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string result;
|
||||
std::string measurement;
|
||||
std::string resulterror = "";
|
||||
std::string resultraw = "";
|
||||
std::string resultrate = "";
|
||||
std::string resulttimestamp = "";
|
||||
long int timeutc;
|
||||
string zw = "";
|
||||
string namenumber = "";
|
||||
std::string result = "";
|
||||
std::string measurement = "";
|
||||
long int result_timeutc = 0;
|
||||
std::string name_number = "";
|
||||
|
||||
if (flowpostprocessing)
|
||||
{
|
||||
std::vector<NumberPost*>* NUMBERS = flowpostprocessing->GetNumbers();
|
||||
std::vector<NumberPost *> *NUMBERS = flowpostprocessing->GetNumbers();
|
||||
|
||||
for (int i = 0; i < (*NUMBERS).size(); ++i)
|
||||
{
|
||||
measurement = (*NUMBERS)[i]->MeasurementV1;
|
||||
result = (*NUMBERS)[i]->ReturnValue;
|
||||
resultraw = (*NUMBERS)[i]->ReturnRawValue;
|
||||
resulterror = (*NUMBERS)[i]->ErrorMessageText;
|
||||
resultrate = (*NUMBERS)[i]->ReturnRateValue;
|
||||
resulttimestamp = (*NUMBERS)[i]->timeStamp;
|
||||
timeutc = (*NUMBERS)[i]->timeStampTimeUTC;
|
||||
result = (*NUMBERS)[i]->ReturnValue;
|
||||
result_timeutc = (*NUMBERS)[i]->timeStampTimeUTC;
|
||||
|
||||
if ((*NUMBERS)[i]->FieldV1.length() > 0)
|
||||
{
|
||||
namenumber = (*NUMBERS)[i]->FieldV1;
|
||||
name_number = (*NUMBERS)[i]->FieldV1;
|
||||
}
|
||||
else
|
||||
{
|
||||
namenumber = (*NUMBERS)[i]->name;
|
||||
if (namenumber == "default")
|
||||
namenumber = "value";
|
||||
name_number = (*NUMBERS)[i]->name;
|
||||
if (name_number == "default")
|
||||
{
|
||||
name_number = "value";
|
||||
}
|
||||
else
|
||||
namenumber = namenumber + "/value";
|
||||
{
|
||||
name_number = name_number + "/value";
|
||||
}
|
||||
}
|
||||
|
||||
if (result.length() > 0)
|
||||
//////////////////////// NEW //////////////////////////
|
||||
// InfluxDBPublish(measurement, namenumber, result, timeutc);
|
||||
influxDB.InfluxDBPublish(measurement, namenumber, result, timeutc);
|
||||
//////////////////////// NEW //////////////////////////
|
||||
|
||||
|
||||
{
|
||||
influxDB.InfluxDBPublish(measurement, name_number, result, result_timeutc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OldValue = result;
|
||||
influxDBv1_controll_config.oldValue = result;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ClassFlowInfluxDB::handleMeasurement(string _decsep, string _value)
|
||||
void ClassFlowInfluxDB::handleMeasurement(std::string _decsep, std::string _value)
|
||||
{
|
||||
string _digit, _decpos;
|
||||
std::string _digit;
|
||||
int _pospunkt = _decsep.find_first_of(".");
|
||||
// ESP_LOGD(TAG, "Name: %s, Pospunkt: %d", _decsep.c_str(), _pospunkt);
|
||||
|
||||
if (_pospunkt > -1)
|
||||
{
|
||||
_digit = _decsep.substr(0, _pospunkt);
|
||||
}
|
||||
else
|
||||
{
|
||||
_digit = "default";
|
||||
}
|
||||
|
||||
for (int j = 0; j < flowpostprocessing->NUMBERS.size(); ++j)
|
||||
{
|
||||
if (_digit == "default") // Set to default first (if nothing else is set)
|
||||
{
|
||||
flowpostprocessing->NUMBERS[j]->MeasurementV1 = _value;
|
||||
}
|
||||
if (flowpostprocessing->NUMBERS[j]->name == _digit)
|
||||
// Set to default first (if nothing else is set)
|
||||
if ((_digit == "default") || (flowpostprocessing->NUMBERS[j]->name == _digit))
|
||||
{
|
||||
flowpostprocessing->NUMBERS[j]->MeasurementV1 = _value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ClassFlowInfluxDB::handleFieldname(string _decsep, string _value)
|
||||
void ClassFlowInfluxDB::handleFieldname(std::string _decsep, std::string _value)
|
||||
{
|
||||
string _digit, _decpos;
|
||||
std::string _digit;
|
||||
int _pospunkt = _decsep.find_first_of(".");
|
||||
// ESP_LOGD(TAG, "Name: %s, Pospunkt: %d", _decsep.c_str(), _pospunkt);
|
||||
|
||||
if (_pospunkt > -1)
|
||||
{
|
||||
_digit = _decsep.substr(0, _pospunkt);
|
||||
}
|
||||
else
|
||||
{
|
||||
_digit = "default";
|
||||
}
|
||||
|
||||
for (int j = 0; j < flowpostprocessing->NUMBERS.size(); ++j)
|
||||
{
|
||||
if (_digit == "default") // Set to default first (if nothing else is set)
|
||||
{
|
||||
flowpostprocessing->NUMBERS[j]->FieldV1 = _value;
|
||||
}
|
||||
if (flowpostprocessing->NUMBERS[j]->name == _digit)
|
||||
// Set to default first (if nothing else is set)
|
||||
if ((_digit == "default") || (flowpostprocessing->NUMBERS[j]->name == _digit))
|
||||
{
|
||||
flowpostprocessing->NUMBERS[j]->FieldV1 = _value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif //ENABLE_INFLUXDB
|
||||
@@ -1,47 +1,47 @@
|
||||
#ifdef ENABLE_INFLUXDB
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef CLASSFINFLUXDB_H
|
||||
#define CLASSFINFLUXDB_H
|
||||
|
||||
#include "ClassFlow.h"
|
||||
#include <string>
|
||||
|
||||
#include "ClassFlow.h"
|
||||
#include "ClassFlowPostProcessing.h"
|
||||
#include "interface_influxdb.h"
|
||||
|
||||
#include <string>
|
||||
typedef struct
|
||||
{
|
||||
bool enabled;
|
||||
std::string uri;
|
||||
std::string database;
|
||||
std::string measurement;
|
||||
std::string user;
|
||||
std::string password;
|
||||
std::string oldValue;
|
||||
|
||||
class ClassFlowInfluxDB :
|
||||
public ClassFlow
|
||||
} influxDBv1_controll_config_t;
|
||||
|
||||
extern influxDBv1_controll_config_t influxDBv1_controll_config;
|
||||
|
||||
class ClassFlowInfluxDB : public ClassFlow
|
||||
{
|
||||
protected:
|
||||
std::string uri, database, measurement;
|
||||
std::string OldValue;
|
||||
ClassFlowPostProcessing* flowpostprocessing;
|
||||
std::string user, password;
|
||||
bool InfluxDBenable;
|
||||
|
||||
InfluxDB influxDB;
|
||||
|
||||
ClassFlowPostProcessing *flowpostprocessing;
|
||||
|
||||
void SetInitialParameter(void);
|
||||
|
||||
void handleFieldname(string _decsep, string _value);
|
||||
void handleMeasurement(string _decsep, string _value);
|
||||
|
||||
|
||||
void handleFieldname(std::string _decsep, std::string _value);
|
||||
void handleMeasurement(std::string _decsep, std::string _value);
|
||||
|
||||
public:
|
||||
ClassFlowInfluxDB();
|
||||
ClassFlowInfluxDB(std::vector<ClassFlow*>* lfc);
|
||||
ClassFlowInfluxDB(std::vector<ClassFlow*>* lfc, ClassFlow *_prev);
|
||||
ClassFlowInfluxDB(std::vector<ClassFlow *> *lfc);
|
||||
ClassFlowInfluxDB(std::vector<ClassFlow *> *lfc, ClassFlow *_prev);
|
||||
|
||||
// string GetInfluxDBMeasurement();
|
||||
|
||||
bool ReadParameter(FILE* pfile, string& aktparamgraph);
|
||||
bool doFlow(string time);
|
||||
string name(){return "ClassFlowInfluxDB";};
|
||||
bool ReadParameter(FILE *pfile, std::string &aktparamgraph);
|
||||
bool doFlow(std::string temp_time);
|
||||
std::string name() { return "ClassFlowInfluxDB"; };
|
||||
};
|
||||
|
||||
#endif //CLASSFINFLUXDB_H
|
||||
#endif //ENABLE_INFLUXDB
|
||||
#endif // CLASSFINFLUXDB_H
|
||||
|
||||
@@ -1,36 +1,38 @@
|
||||
#ifdef ENABLE_INFLUXDB
|
||||
#include "defines.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <time.h>
|
||||
|
||||
#include "ClassFlowInfluxDBv2.h"
|
||||
|
||||
#include "Helper.h"
|
||||
#include "connect_wlan.h"
|
||||
#include "connect_wifi_sta.h"
|
||||
|
||||
#include "time_sntp.h"
|
||||
#include "interface_influxdb.h"
|
||||
|
||||
#include "ClassFlowPostProcessing.h"
|
||||
#include "esp_log.h"
|
||||
#include "../../include/defines.h"
|
||||
|
||||
#include "ClassLogFile.h"
|
||||
|
||||
#include <time.h>
|
||||
static const char *TAG = "INFLUXDBV2";
|
||||
|
||||
static const char* TAG = "INFLUXDBV2";
|
||||
influxDBv2_controll_config_t influxDBv2_controll_config;
|
||||
|
||||
void ClassFlowInfluxDBv2::SetInitialParameter(void)
|
||||
{
|
||||
uri = "";
|
||||
bucket = "";
|
||||
dborg = "";
|
||||
dbtoken = "";
|
||||
// dbfield = "";
|
||||
influxDBv2_controll_config.enabled = false;
|
||||
influxDBv2_controll_config.uri = "";
|
||||
influxDBv2_controll_config.bucket = "";
|
||||
influxDBv2_controll_config.dborg = "";
|
||||
influxDBv2_controll_config.dbtoken = "";
|
||||
influxDBv2_controll_config.oldValue = "";
|
||||
|
||||
OldValue = "";
|
||||
flowpostprocessing = NULL;
|
||||
previousElement = NULL;
|
||||
ListFlowControll = NULL;
|
||||
|
||||
disabled = false;
|
||||
InfluxDBenable = false;
|
||||
}
|
||||
|
||||
ClassFlowInfluxDBv2::ClassFlowInfluxDBv2()
|
||||
@@ -38,24 +40,23 @@ ClassFlowInfluxDBv2::ClassFlowInfluxDBv2()
|
||||
SetInitialParameter();
|
||||
}
|
||||
|
||||
ClassFlowInfluxDBv2::ClassFlowInfluxDBv2(std::vector<ClassFlow*>* lfc)
|
||||
ClassFlowInfluxDBv2::ClassFlowInfluxDBv2(std::vector<ClassFlow *> *lfc)
|
||||
{
|
||||
SetInitialParameter();
|
||||
|
||||
ListFlowControll = lfc;
|
||||
|
||||
for (int i = 0; i < ListFlowControll->size(); ++i)
|
||||
{
|
||||
if (((*ListFlowControll)[i])->name().compare("ClassFlowPostProcessing") == 0)
|
||||
{
|
||||
flowpostprocessing = (ClassFlowPostProcessing*) (*ListFlowControll)[i];
|
||||
flowpostprocessing = (ClassFlowPostProcessing *)(*ListFlowControll)[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ClassFlowInfluxDBv2::ClassFlowInfluxDBv2(std::vector<ClassFlow*>* lfc, ClassFlow *_prev)
|
||||
ClassFlowInfluxDBv2::ClassFlowInfluxDBv2(std::vector<ClassFlow *> *lfc, ClassFlow *_prev)
|
||||
{
|
||||
SetInitialParameter();
|
||||
|
||||
previousElement = _prev;
|
||||
ListFlowControll = lfc;
|
||||
|
||||
@@ -63,190 +64,182 @@ ClassFlowInfluxDBv2::ClassFlowInfluxDBv2(std::vector<ClassFlow*>* lfc, ClassFlow
|
||||
{
|
||||
if (((*ListFlowControll)[i])->name().compare("ClassFlowPostProcessing") == 0)
|
||||
{
|
||||
flowpostprocessing = (ClassFlowPostProcessing*) (*ListFlowControll)[i];
|
||||
flowpostprocessing = (ClassFlowPostProcessing *)(*ListFlowControll)[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool ClassFlowInfluxDBv2::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
bool ClassFlowInfluxDBv2::ReadParameter(FILE *pFile, std::string &aktparamgraph)
|
||||
{
|
||||
std::vector<string> splitted;
|
||||
|
||||
aktparamgraph = trim(aktparamgraph);
|
||||
printf("akt param: %s\n", aktparamgraph.c_str());
|
||||
|
||||
aktparamgraph = trim_string_left_right(aktparamgraph);
|
||||
if (aktparamgraph.size() == 0)
|
||||
if (!this->GetNextParagraph(pfile, aktparamgraph))
|
||||
return false;
|
||||
|
||||
if (toUpper(aktparamgraph).compare("[INFLUXDBV2]") != 0)
|
||||
return false;
|
||||
|
||||
while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph))
|
||||
{
|
||||
// ESP_LOGD(TAG, "while loop reading line: %s", aktparamgraph.c_str());
|
||||
splitted = ZerlegeZeile(aktparamgraph);
|
||||
std::string _param = GetParameterName(splitted[0]);
|
||||
|
||||
if ((toUpper(_param) == "ORG") && (splitted.size() > 1))
|
||||
if (!GetNextParagraph(pFile, aktparamgraph))
|
||||
{
|
||||
this->dborg = splitted[1];
|
||||
}
|
||||
if ((toUpper(_param) == "TOKEN") && (splitted.size() > 1))
|
||||
{
|
||||
this->dbtoken = splitted[1];
|
||||
}
|
||||
if ((toUpper(_param) == "URI") && (splitted.size() > 1))
|
||||
{
|
||||
this->uri = splitted[1];
|
||||
}
|
||||
if (((toUpper(_param) == "FIELD")) && (splitted.size() > 1))
|
||||
{
|
||||
handleFieldname(splitted[0], splitted[1]);
|
||||
}
|
||||
if (((toUpper(_param) == "MEASUREMENT")) && (splitted.size() > 1))
|
||||
{
|
||||
handleMeasurement(splitted[0], splitted[1]);
|
||||
}
|
||||
if (((toUpper(splitted[0]) == "BUCKET")) && (splitted.size() > 1))
|
||||
{
|
||||
this->bucket = splitted[1];
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
printf("uri: %s\n", uri.c_str());
|
||||
printf("org: %s\n", dborg.c_str());
|
||||
printf("token: %s\n", dbtoken.c_str());
|
||||
|
||||
if ((uri.length() > 0) && (bucket.length() > 0) && (dbtoken.length() > 0) && (dborg.length() > 0))
|
||||
if ((to_upper(aktparamgraph).compare("[INFLUXDBV2]") != 0) && (to_upper(aktparamgraph).compare(";[INFLUXDBV2]") != 0))
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Init InfluxDB with uri: " + uri + ", org: " + dborg + ", token: *****");
|
||||
// printf("vor V2 Init\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (aktparamgraph[0] == ';')
|
||||
{
|
||||
influxDBv2_controll_config.enabled = false;
|
||||
while (getNextLine(pFile, &aktparamgraph) && !isNewParagraph(aktparamgraph));
|
||||
ESP_LOGD(TAG, "InfluxDBv2 is disabled!");
|
||||
|
||||
////////////////////////////////////////// NEW ////////////////////////////////////////////
|
||||
// InfluxDB_V2_Init(uri, bucket, dborg, dbtoken);
|
||||
// InfluxDB_V2_Init(uri, bucket, dborg, dbtoken);
|
||||
influxdb.InfluxDBInitV2(uri, bucket, dborg, dbtoken);
|
||||
////////////////////////////////////////// NEW ////////////////////////////////////////////
|
||||
return true;
|
||||
}
|
||||
|
||||
// printf("nach V2 Init\n");
|
||||
InfluxDBenable = true;
|
||||
} else {
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "InfluxDBv2 (Verion2 !!!) init skipped as we are missing some parameters");
|
||||
std::vector<std::string> splitted;
|
||||
|
||||
while (getNextLine(pFile, &aktparamgraph) && !isNewParagraph(aktparamgraph))
|
||||
{
|
||||
splitted = split_line(aktparamgraph);
|
||||
|
||||
if (splitted.size() > 1)
|
||||
{
|
||||
std::string _param = to_upper(GetParameterName(splitted[0]));
|
||||
|
||||
if (_param == "ORG")
|
||||
{
|
||||
influxDBv2_controll_config.dborg = splitted[1];
|
||||
}
|
||||
else if (_param == "TOKEN")
|
||||
{
|
||||
influxDBv2_controll_config.dbtoken = splitted[1];
|
||||
}
|
||||
else if (_param == "URI")
|
||||
{
|
||||
influxDBv2_controll_config.uri = splitted[1];
|
||||
}
|
||||
else if (_param == "FIELD")
|
||||
{
|
||||
handleFieldname(splitted[0], splitted[1]);
|
||||
}
|
||||
else if (_param == "MEASUREMENT")
|
||||
{
|
||||
handleMeasurement(splitted[0], splitted[1]);
|
||||
}
|
||||
else if (_param == "BUCKET")
|
||||
{
|
||||
influxDBv2_controll_config.bucket = splitted[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((influxDBv2_controll_config.uri.length() > 0) && (influxDBv2_controll_config.bucket.length() > 0) && (influxDBv2_controll_config.dbtoken.length() > 0) && (influxDBv2_controll_config.dborg.length() > 0))
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Init InfluxDBv2 with uri: " + influxDBv2_controll_config.uri + ", org: " + influxDBv2_controll_config.dborg + ", token: *****");
|
||||
influxDB.InfluxDBInitV2(influxDBv2_controll_config.uri, influxDBv2_controll_config.bucket, influxDBv2_controll_config.dborg, influxDBv2_controll_config.dbtoken);
|
||||
influxDBv2_controll_config.enabled = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "InfluxDBv2 init skipped as we are missing some parameters");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
string ClassFlowInfluxDBv2::GetInfluxDBMeasurement()
|
||||
bool ClassFlowInfluxDBv2::doFlow(std::string temp_time)
|
||||
{
|
||||
return measurement;
|
||||
}
|
||||
*/
|
||||
|
||||
void ClassFlowInfluxDBv2::handleFieldname(string _decsep, string _value)
|
||||
{
|
||||
string _digit, _decpos;
|
||||
int _pospunkt = _decsep.find_first_of(".");
|
||||
// ESP_LOGD(TAG, "Name: %s, Pospunkt: %d", _decsep.c_str(), _pospunkt);
|
||||
if (_pospunkt > -1)
|
||||
_digit = _decsep.substr(0, _pospunkt);
|
||||
else
|
||||
_digit = "default";
|
||||
for (int j = 0; j < flowpostprocessing->NUMBERS.size(); ++j)
|
||||
if (!influxDBv2_controll_config.enabled)
|
||||
{
|
||||
if (_digit == "default") // Set to default first (if nothing else is set)
|
||||
{
|
||||
flowpostprocessing->NUMBERS[j]->FieldV2 = _value;
|
||||
}
|
||||
if (flowpostprocessing->NUMBERS[j]->name == _digit)
|
||||
{
|
||||
flowpostprocessing->NUMBERS[j]->FieldV2 = _value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ClassFlowInfluxDBv2::handleMeasurement(string _decsep, string _value)
|
||||
{
|
||||
string _digit, _decpos;
|
||||
int _pospunkt = _decsep.find_first_of(".");
|
||||
// ESP_LOGD(TAG, "Name: %s, Pospunkt: %d", _decsep.c_str(), _pospunkt);
|
||||
if (_pospunkt > -1)
|
||||
_digit = _decsep.substr(0, _pospunkt);
|
||||
else
|
||||
_digit = "default";
|
||||
for (int j = 0; j < flowpostprocessing->NUMBERS.size(); ++j)
|
||||
{
|
||||
if (_digit == "default") // Set to default first (if nothing else is set)
|
||||
{
|
||||
flowpostprocessing->NUMBERS[j]->MeasurementV2 = _value;
|
||||
}
|
||||
if (flowpostprocessing->NUMBERS[j]->name == _digit)
|
||||
{
|
||||
flowpostprocessing->NUMBERS[j]->MeasurementV2 = _value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool ClassFlowInfluxDBv2::doFlow(string zwtime)
|
||||
{
|
||||
if (!InfluxDBenable)
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string measurement;
|
||||
std::string result;
|
||||
std::string resulterror = "";
|
||||
std::string resultraw = "";
|
||||
std::string resultrate = "";
|
||||
std::string resulttimestamp = "";
|
||||
long int resulttimeutc = 0;
|
||||
string zw = "";
|
||||
string namenumber = "";
|
||||
|
||||
std::string measurement = "";
|
||||
std::string result = "";
|
||||
long int result_timeutc = 0;
|
||||
std::string name_number = "";
|
||||
|
||||
if (flowpostprocessing)
|
||||
{
|
||||
std::vector<NumberPost*>* NUMBERS = flowpostprocessing->GetNumbers();
|
||||
std::vector<NumberPost *> *NUMBERS = flowpostprocessing->GetNumbers();
|
||||
|
||||
for (int i = 0; i < (*NUMBERS).size(); ++i)
|
||||
{
|
||||
measurement = (*NUMBERS)[i]->MeasurementV2;
|
||||
result = (*NUMBERS)[i]->ReturnValue;
|
||||
resultraw = (*NUMBERS)[i]->ReturnRawValue;
|
||||
resulterror = (*NUMBERS)[i]->ErrorMessageText;
|
||||
resultrate = (*NUMBERS)[i]->ReturnRateValue;
|
||||
resulttimestamp = (*NUMBERS)[i]->timeStamp;
|
||||
resulttimeutc = (*NUMBERS)[i]->timeStampTimeUTC;
|
||||
|
||||
result = (*NUMBERS)[i]->ReturnValue;
|
||||
result_timeutc = (*NUMBERS)[i]->timeStampTimeUTC;
|
||||
|
||||
if ((*NUMBERS)[i]->FieldV2.length() > 0)
|
||||
{
|
||||
namenumber = (*NUMBERS)[i]->FieldV2;
|
||||
name_number = (*NUMBERS)[i]->FieldV2;
|
||||
}
|
||||
else
|
||||
{
|
||||
namenumber = (*NUMBERS)[i]->name;
|
||||
if (namenumber == "default")
|
||||
namenumber = "value";
|
||||
name_number = (*NUMBERS)[i]->name;
|
||||
if (name_number == "default")
|
||||
{
|
||||
name_number = "value";
|
||||
}
|
||||
else
|
||||
namenumber = namenumber + "/value";
|
||||
{
|
||||
name_number = name_number + "/value";
|
||||
}
|
||||
}
|
||||
|
||||
printf("vor sende Influx_DB_V2 - namenumber. %s, result: %s, timestampt: %s", namenumber.c_str(), result.c_str(), resulttimestamp.c_str());
|
||||
|
||||
if (result.length() > 0)
|
||||
influxdb.InfluxDBPublish(measurement, namenumber, result, resulttimeutc);
|
||||
// InfluxDB_V2_Publish(measurement, namenumber, result, resulttimeutc);
|
||||
{
|
||||
influxDB.InfluxDBPublish(measurement, name_number, result, result_timeutc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OldValue = result;
|
||||
influxDBv2_controll_config.oldValue = result;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif //ENABLE_INFLUXDB
|
||||
void ClassFlowInfluxDBv2::handleFieldname(std::string _decsep, std::string _value)
|
||||
{
|
||||
std::string _digit;
|
||||
int _pospunkt = _decsep.find_first_of(".");
|
||||
|
||||
if (_pospunkt > -1)
|
||||
{
|
||||
_digit = _decsep.substr(0, _pospunkt);
|
||||
}
|
||||
else
|
||||
{
|
||||
_digit = "default";
|
||||
}
|
||||
|
||||
for (int j = 0; j < flowpostprocessing->NUMBERS.size(); ++j)
|
||||
{
|
||||
// Set to default first (if nothing else is set)
|
||||
if ((_digit == "default") || (flowpostprocessing->NUMBERS[j]->name == _digit))
|
||||
{
|
||||
flowpostprocessing->NUMBERS[j]->FieldV2 = _value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ClassFlowInfluxDBv2::handleMeasurement(std::string _decsep, std::string _value)
|
||||
{
|
||||
std::string _digit;
|
||||
int _pospunkt = _decsep.find_first_of(".");
|
||||
|
||||
if (_pospunkt > -1)
|
||||
{
|
||||
_digit = _decsep.substr(0, _pospunkt);
|
||||
}
|
||||
else
|
||||
{
|
||||
_digit = "default";
|
||||
}
|
||||
|
||||
for (int j = 0; j < flowpostprocessing->NUMBERS.size(); ++j)
|
||||
{
|
||||
// Set to default first (if nothing else is set)
|
||||
if ((_digit == "default") || (flowpostprocessing->NUMBERS[j]->name == _digit))
|
||||
{
|
||||
flowpostprocessing->NUMBERS[j]->MeasurementV2 = _value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,47 +1,46 @@
|
||||
#ifdef ENABLE_INFLUXDB
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef CLASSFINFLUXDBv2_H
|
||||
#define CLASSFINFLUXDBv2_H
|
||||
|
||||
#include "ClassFlow.h"
|
||||
|
||||
#include "ClassFlowPostProcessing.h"
|
||||
|
||||
#include "interface_influxdb.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
class ClassFlowInfluxDBv2 :
|
||||
public ClassFlow
|
||||
#include "ClassFlow.h"
|
||||
#include "ClassFlowPostProcessing.h"
|
||||
#include "interface_influxdb.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
bool enabled;
|
||||
std::string uri;
|
||||
std::string bucket;
|
||||
std::string dborg;
|
||||
std::string dbtoken;
|
||||
std::string dbfield;
|
||||
std::string oldValue;
|
||||
|
||||
} influxDBv2_controll_config_t;
|
||||
|
||||
extern influxDBv2_controll_config_t influxDBv2_controll_config;
|
||||
|
||||
class ClassFlowInfluxDBv2 : public ClassFlow
|
||||
{
|
||||
protected:
|
||||
std::string uri, bucket;
|
||||
std::string dborg, dbtoken, dbfield;
|
||||
std::string OldValue;
|
||||
ClassFlowPostProcessing* flowpostprocessing;
|
||||
bool InfluxDBenable;
|
||||
|
||||
InfluxDB influxdb;
|
||||
InfluxDB influxDB;
|
||||
ClassFlowPostProcessing *flowpostprocessing;
|
||||
|
||||
void SetInitialParameter(void);
|
||||
|
||||
void handleFieldname(string _decsep, string _value);
|
||||
void handleMeasurement(string _decsep, string _value);
|
||||
|
||||
void handleFieldname(std::string _decsep, std::string _value);
|
||||
void handleMeasurement(std::string _decsep, std::string _value);
|
||||
|
||||
public:
|
||||
ClassFlowInfluxDBv2();
|
||||
ClassFlowInfluxDBv2(std::vector<ClassFlow*>* lfc);
|
||||
ClassFlowInfluxDBv2(std::vector<ClassFlow*>* lfc, ClassFlow *_prev);
|
||||
ClassFlowInfluxDBv2(std::vector<ClassFlow *> *lfc);
|
||||
ClassFlowInfluxDBv2(std::vector<ClassFlow *> *lfc, ClassFlow *_prev);
|
||||
|
||||
// string GetInfluxDBMeasurement();
|
||||
|
||||
bool ReadParameter(FILE* pfile, string& aktparamgraph);
|
||||
bool doFlow(string time);
|
||||
string name(){return "ClassFlowInfluxDBv2";};
|
||||
bool ReadParameter(FILE *pFile, std::string &aktparamgraph);
|
||||
bool doFlow(std::string temp_time);
|
||||
std::string name() { return "ClassFlowInfluxDBv2"; };
|
||||
};
|
||||
|
||||
#endif //CLASSFINFLUXDBv2_H
|
||||
#endif //ENABLE_INFLUXDB
|
||||
#endif // CLASSFINFLUXDBv2_H
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
#ifdef ENABLE_MQTT
|
||||
#include "defines.h"
|
||||
|
||||
#include "ClassFlowMQTT.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include "ClassFlowMQTT.h"
|
||||
#include <time.h>
|
||||
|
||||
#include "Helper.h"
|
||||
#include "connect_wlan.h"
|
||||
#include "read_wlanini.h"
|
||||
|
||||
#include "connect_wifi_sta.h"
|
||||
#include "read_network_config.h"
|
||||
|
||||
#include "ClassLogFile.h"
|
||||
|
||||
#include "time_sntp.h"
|
||||
@@ -15,52 +20,73 @@
|
||||
|
||||
#include "server_mqtt.h"
|
||||
|
||||
#include <time.h>
|
||||
#include "../../include/defines.h"
|
||||
|
||||
static const char *TAG = "MQTT";
|
||||
|
||||
extern const char* libfive_git_version(void);
|
||||
extern const char* libfive_git_revision(void);
|
||||
extern const char* libfive_git_branch(void);
|
||||
mqtt_controll_config_t mqtt_controll_config;
|
||||
|
||||
extern const char *libfive_git_version(void);
|
||||
extern const char *libfive_git_revision(void);
|
||||
extern const char *libfive_git_branch(void);
|
||||
|
||||
void ClassFlowMQTT::SetInitialParameter(void)
|
||||
{
|
||||
uri = "";
|
||||
topic = "";
|
||||
topicError = "";
|
||||
topicRate = "";
|
||||
topicTimeStamp = "";
|
||||
maintopic = wlan_config.hostname;
|
||||
mqtt_controll_config.mqtt_enabled = false;
|
||||
mqtt_controll_config.mqtt_configOK = false;
|
||||
mqtt_controll_config.mqtt_initialized = false;
|
||||
mqtt_controll_config.mqtt_connected = false;
|
||||
|
||||
topicUptime = "";
|
||||
topicFreeMem = "";
|
||||
mqtt_controll_config.HomeAssistantDiscovery = false;
|
||||
|
||||
caCertFilename = "";
|
||||
clientCertFilename = "";
|
||||
clientKeyFilename = "";
|
||||
validateServerCert = true;
|
||||
clientname = wlan_config.hostname;
|
||||
mqtt_controll_config.esp_mqtt_ID = MQTT_EVENT_ANY;
|
||||
|
||||
mqtt_controll_config.uri = "";
|
||||
mqtt_controll_config.topic = "";
|
||||
mqtt_controll_config.topicError = "";
|
||||
mqtt_controll_config.topicRate = "";
|
||||
mqtt_controll_config.topicTimeStamp = "";
|
||||
mqtt_controll_config.maintopic = network_config.hostname;
|
||||
mqtt_controll_config.discoveryprefix = "homeassistant";
|
||||
|
||||
mqtt_controll_config.topicUptime = "";
|
||||
mqtt_controll_config.topicFreeMem = "";
|
||||
|
||||
mqtt_controll_config.caCertFilename = "";
|
||||
mqtt_controll_config.clientCertFilename = "";
|
||||
mqtt_controll_config.clientKeyFilename = "";
|
||||
mqtt_controll_config.validateServerCert = true;
|
||||
mqtt_controll_config.clientname = network_config.hostname;
|
||||
|
||||
mqtt_controll_config.OldValue = "";
|
||||
|
||||
mqtt_controll_config.user = "";
|
||||
mqtt_controll_config.password = "";
|
||||
|
||||
mqtt_controll_config.lwt_topic = mqtt_controll_config.maintopic + "/" + LWT_TOPIC;
|
||||
mqtt_controll_config.lwt_connected = LWT_CONNECTED;
|
||||
mqtt_controll_config.lwt_disconnected = LWT_DISCONNECTED;
|
||||
|
||||
mqtt_controll_config.meterType = "";
|
||||
mqtt_controll_config.valueUnit = "";
|
||||
mqtt_controll_config.timeUnit = "";
|
||||
mqtt_controll_config.rateUnit = "Unit/Minute";
|
||||
|
||||
mqtt_controll_config.retainFlag = false;
|
||||
mqtt_controll_config.keepAlive = 25 * 60;
|
||||
mqtt_controll_config.domoticzintopic = "";
|
||||
|
||||
OldValue = "";
|
||||
flowpostprocessing = NULL;
|
||||
user = "";
|
||||
password = "";
|
||||
SetRetainFlag = false;
|
||||
previousElement = NULL;
|
||||
ListFlowControll = NULL;
|
||||
|
||||
disabled = false;
|
||||
keepAlive = 25*60;
|
||||
domoticzintopic = "";
|
||||
}
|
||||
|
||||
ClassFlowMQTT::ClassFlowMQTT()
|
||||
ClassFlowMQTT::ClassFlowMQTT(void)
|
||||
{
|
||||
SetInitialParameter();
|
||||
}
|
||||
|
||||
ClassFlowMQTT::ClassFlowMQTT(std::vector<ClassFlow*>* lfc)
|
||||
ClassFlowMQTT::ClassFlowMQTT(std::vector<ClassFlow *> *lfc)
|
||||
{
|
||||
SetInitialParameter();
|
||||
|
||||
@@ -69,12 +95,12 @@ ClassFlowMQTT::ClassFlowMQTT(std::vector<ClassFlow*>* lfc)
|
||||
{
|
||||
if (((*ListFlowControll)[i])->name().compare("ClassFlowPostProcessing") == 0)
|
||||
{
|
||||
flowpostprocessing = (ClassFlowPostProcessing*) (*ListFlowControll)[i];
|
||||
flowpostprocessing = (ClassFlowPostProcessing *)(*ListFlowControll)[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ClassFlowMQTT::ClassFlowMQTT(std::vector<ClassFlow*>* lfc, ClassFlow *_prev)
|
||||
ClassFlowMQTT::ClassFlowMQTT(std::vector<ClassFlow *> *lfc, ClassFlow *_prev)
|
||||
{
|
||||
SetInitialParameter();
|
||||
|
||||
@@ -85,301 +111,348 @@ ClassFlowMQTT::ClassFlowMQTT(std::vector<ClassFlow*>* lfc, ClassFlow *_prev)
|
||||
{
|
||||
if (((*ListFlowControll)[i])->name().compare("ClassFlowPostProcessing") == 0)
|
||||
{
|
||||
flowpostprocessing = (ClassFlowPostProcessing*) (*ListFlowControll)[i];
|
||||
flowpostprocessing = (ClassFlowPostProcessing *)(*ListFlowControll)[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool ClassFlowMQTT::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
bool ClassFlowMQTT::ReadParameter(FILE *pFile, std::string &aktparamgraph)
|
||||
{
|
||||
std::vector<string> splitted;
|
||||
|
||||
aktparamgraph = trim(aktparamgraph);
|
||||
|
||||
aktparamgraph = trim_string_left_right(aktparamgraph);
|
||||
if (aktparamgraph.size() == 0)
|
||||
if (!this->GetNextParagraph(pfile, aktparamgraph))
|
||||
return false;
|
||||
|
||||
if (toUpper(aktparamgraph).compare("[MQTT]") != 0) // Paragraph does not fit MQTT
|
||||
return false;
|
||||
|
||||
while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph))
|
||||
{
|
||||
splitted = ZerlegeZeile(aktparamgraph);
|
||||
std::string _param = GetParameterName(splitted[0]);
|
||||
if ((toUpper(_param) == "CACERT") && (splitted.size() > 1))
|
||||
if (!GetNextParagraph(pFile, aktparamgraph))
|
||||
{
|
||||
this->caCertFilename = "/sdcard" + splitted[1];
|
||||
return false;
|
||||
}
|
||||
if ((toUpper(_param) == "VALIDATESERVERCERT") && (splitted.size() > 1))
|
||||
{
|
||||
validateServerCert = alphanumericToBoolean(splitted[1]);
|
||||
}
|
||||
if ((toUpper(_param) == "CLIENTCERT") && (splitted.size() > 1))
|
||||
{
|
||||
this->clientCertFilename = "/sdcard" + splitted[1];
|
||||
}
|
||||
if ((toUpper(_param) == "CLIENTKEY") && (splitted.size() > 1))
|
||||
{
|
||||
this->clientKeyFilename = "/sdcard" + splitted[1];
|
||||
}
|
||||
if ((toUpper(_param) == "USER") && (splitted.size() > 1))
|
||||
{
|
||||
this->user = splitted[1];
|
||||
}
|
||||
if ((toUpper(_param) == "PASSWORD") && (splitted.size() > 1))
|
||||
{
|
||||
this->password = splitted[1];
|
||||
}
|
||||
if ((toUpper(_param) == "URI") && (splitted.size() > 1))
|
||||
{
|
||||
this->uri = splitted[1];
|
||||
}
|
||||
if ((toUpper(_param) == "RETAINMESSAGES") && (splitted.size() > 1))
|
||||
{
|
||||
SetRetainFlag = alphanumericToBoolean(splitted[1]);
|
||||
setMqtt_Server_Retain(SetRetainFlag);
|
||||
}
|
||||
if ((toUpper(_param) == "HOMEASSISTANTDISCOVERY") && (splitted.size() > 1))
|
||||
{
|
||||
if (toUpper(splitted[1]) == "TRUE")
|
||||
SetHomeassistantDiscoveryEnabled(true);
|
||||
}
|
||||
if ((toUpper(_param) == "METERTYPE") && (splitted.size() > 1)) {
|
||||
/* Use meter type for the device class
|
||||
Make sure it is a listed one on https://developers.home-assistant.io/docs/core/entity/sensor/#available-device-classes */
|
||||
if (toUpper(splitted[1]) == "WATER_M3") {
|
||||
mqttServer_setMeterType("water", "m³", "h", "m³/h");
|
||||
}
|
||||
else if (toUpper(splitted[1]) == "WATER_L") {
|
||||
mqttServer_setMeterType("water", "L", "h", "L/h");
|
||||
}
|
||||
else if (toUpper(splitted[1]) == "WATER_FT3") {
|
||||
mqttServer_setMeterType("water", "ft³", "min", "ft³/min"); // min = Minutes
|
||||
}
|
||||
else if (toUpper(splitted[1]) == "WATER_GAL") {
|
||||
mqttServer_setMeterType("water", "gal", "h", "gal/h");
|
||||
}
|
||||
else if (toUpper(splitted[1]) == "WATER_GAL_MIN") {
|
||||
mqttServer_setMeterType("water", "gal", "min", "gal/min"); // min = Minutes
|
||||
}
|
||||
else if (toUpper(splitted[1]) == "GAS_M3") {
|
||||
mqttServer_setMeterType("gas", "m³", "h", "m³/h");
|
||||
}
|
||||
else if (toUpper(splitted[1]) == "GAS_FT3") {
|
||||
mqttServer_setMeterType("gas", "ft³", "min", "ft³/min"); // min = Minutes
|
||||
}
|
||||
else if (toUpper(splitted[1]) == "ENERGY_WH") {
|
||||
mqttServer_setMeterType("energy", "Wh", "h", "W");
|
||||
}
|
||||
else if (toUpper(splitted[1]) == "ENERGY_KWH") {
|
||||
mqttServer_setMeterType("energy", "kWh", "h", "kW");
|
||||
}
|
||||
else if (toUpper(splitted[1]) == "ENERGY_MWH") {
|
||||
mqttServer_setMeterType("energy", "MWh", "h", "MW");
|
||||
}
|
||||
else if (toUpper(splitted[1]) == "ENERGY_GJ") {
|
||||
mqttServer_setMeterType("energy", "GJ", "h", "GJ/h");
|
||||
}
|
||||
else if (toUpper(splitted[1]) == "TEMPERATURE_C") {
|
||||
mqttServer_setMeterType("temperature", "°C", "min", "°C/min"); // min = Minutes
|
||||
}
|
||||
else if (toUpper(splitted[1]) == "TEMPERATURE_F") {
|
||||
mqttServer_setMeterType("temperature", "°F", "min", "°F/min"); // min = Minutes
|
||||
}
|
||||
else if (toUpper(splitted[1]) == "TEMPERATURE_K") {
|
||||
mqttServer_setMeterType("temperature", "K", "min", "K/m"); // min = Minutes
|
||||
}
|
||||
}
|
||||
|
||||
if ((toUpper(_param) == "CLIENTID") && (splitted.size() > 1))
|
||||
{
|
||||
this->clientname = splitted[1];
|
||||
}
|
||||
|
||||
if (((toUpper(_param) == "TOPIC") || (toUpper(splitted[0]) == "MAINTOPIC")) && (splitted.size() > 1))
|
||||
{
|
||||
maintopic = splitted[1];
|
||||
}
|
||||
|
||||
if (((toUpper(_param) == "DOMOTICZTOPICIN")) && (splitted.size() > 1))
|
||||
{
|
||||
this->domoticzintopic = splitted[1];
|
||||
}
|
||||
|
||||
if (((toUpper(_param) == "DOMOTICZIDX")) && (splitted.size() > 1))
|
||||
{
|
||||
handleIdx(splitted[0], splitted[1]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Note:
|
||||
* Originally, we started the MQTT client here.
|
||||
* How ever we need the interval parameter from the ClassFlowControll, but that only gets started later.
|
||||
* To work around this, we delay the start and trigger it from ClassFlowControll::ReadParameter() */
|
||||
if ((to_upper(aktparamgraph).compare("[MQTT]") != 0) && (to_upper(aktparamgraph).compare(";[MQTT]") != 0))
|
||||
{
|
||||
// Paragraph does not fit MQTT
|
||||
return false;
|
||||
}
|
||||
|
||||
mqttServer_setMainTopic(maintopic);
|
||||
mqttServer_setDmoticzInTopic(domoticzintopic);
|
||||
if (aktparamgraph[0] == ';')
|
||||
{
|
||||
mqtt_controll_config.mqtt_enabled = false;
|
||||
while (getNextLine(pFile, &aktparamgraph) && !isNewParagraph(aktparamgraph));
|
||||
ESP_LOGD(TAG, "mqtt is disabled!");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<std::string> splitted;
|
||||
|
||||
while (getNextLine(pFile, &aktparamgraph) && !isNewParagraph(aktparamgraph))
|
||||
{
|
||||
splitted = split_line(aktparamgraph);
|
||||
|
||||
if (splitted.size() > 1)
|
||||
{
|
||||
std::string _param = to_upper(GetParameterName(splitted[0]));
|
||||
|
||||
if (_param == "CACERT")
|
||||
{
|
||||
mqtt_controll_config.caCertFilename = "/sdcard" + splitted[1];
|
||||
}
|
||||
else if (_param == "VALIDATESERVERCERT")
|
||||
{
|
||||
mqtt_controll_config.validateServerCert = alphanumeric_to_boolean(splitted[1]);
|
||||
}
|
||||
else if (_param == "CLIENTCERT")
|
||||
{
|
||||
mqtt_controll_config.clientCertFilename = "/sdcard" + splitted[1];
|
||||
}
|
||||
else if (_param == "CLIENTKEY")
|
||||
{
|
||||
mqtt_controll_config.clientKeyFilename = "/sdcard" + splitted[1];
|
||||
}
|
||||
else if (_param == "USER")
|
||||
{
|
||||
mqtt_controll_config.user = splitted[1];
|
||||
}
|
||||
else if (_param == "PASSWORD")
|
||||
{
|
||||
mqtt_controll_config.password = splitted[1];
|
||||
}
|
||||
else if (_param == "URI")
|
||||
{
|
||||
mqtt_controll_config.uri = splitted[1];
|
||||
}
|
||||
else if (_param == "RETAINMESSAGES")
|
||||
{
|
||||
mqtt_controll_config.retainFlag = alphanumeric_to_boolean(splitted[1]);
|
||||
}
|
||||
else if (_param == "HOMEASSISTANTDISCOVERY")
|
||||
{
|
||||
if (to_upper(splitted[1]) == "TRUE")
|
||||
{
|
||||
mqtt_controll_config.HomeAssistantDiscovery = true;
|
||||
}
|
||||
}
|
||||
else if (_param == "METERTYPE")
|
||||
{
|
||||
std::string _value = to_upper(splitted[1]);
|
||||
|
||||
/* Use meter type for the device class
|
||||
Make sure it is a listed one on https://developers.home-assistant.io/docs/core/entity/sensor/#available-device-classes */
|
||||
if (_value == "WATER_M3")
|
||||
{
|
||||
SetMeterType("water", "m³", "h", "m³/h");
|
||||
}
|
||||
else if (_value == "WATER_L")
|
||||
{
|
||||
SetMeterType("water", "L", "h", "L/h");
|
||||
}
|
||||
else if (_value == "WATER_FT3")
|
||||
{
|
||||
SetMeterType("water", "ft³", "min", "ft³/min"); // min = Minutes
|
||||
}
|
||||
else if (_value == "WATER_GAL")
|
||||
{
|
||||
SetMeterType("water", "gal", "h", "gal/h");
|
||||
}
|
||||
else if (_value == "WATER_GAL_MIN")
|
||||
{
|
||||
SetMeterType("water", "gal", "min", "gal/min"); // min = Minutes
|
||||
}
|
||||
else if (_value == "GAS_M3")
|
||||
{
|
||||
SetMeterType("gas", "m³", "h", "m³/h");
|
||||
}
|
||||
else if (_value == "GAS_FT3")
|
||||
{
|
||||
SetMeterType("gas", "ft³", "min", "ft³/min"); // min = Minutes
|
||||
}
|
||||
else if (_value == "ENERGY_WH")
|
||||
{
|
||||
SetMeterType("energy", "Wh", "h", "W");
|
||||
}
|
||||
else if (_value == "ENERGY_KWH")
|
||||
{
|
||||
SetMeterType("energy", "kWh", "h", "kW");
|
||||
}
|
||||
else if (_value == "ENERGY_MWH")
|
||||
{
|
||||
SetMeterType("energy", "MWh", "h", "MW");
|
||||
}
|
||||
else if (_value == "ENERGY_GJ")
|
||||
{
|
||||
SetMeterType("energy", "GJ", "h", "GJ/h");
|
||||
}
|
||||
else if (_value == "TEMPERATURE_C")
|
||||
{
|
||||
SetMeterType("temperature", "°C", "min", "°C/min"); // min = Minutes
|
||||
}
|
||||
else if (_value == "TEMPERATURE_F")
|
||||
{
|
||||
SetMeterType("temperature", "°F", "min", "°F/min"); // min = Minutes
|
||||
}
|
||||
else if (_value == "TEMPERATURE_K")
|
||||
{
|
||||
SetMeterType("temperature", "K", "min", "K/m"); // min = Minutes
|
||||
}
|
||||
}
|
||||
else if (_param == "CLIENTID")
|
||||
{
|
||||
mqtt_controll_config.clientname = splitted[1];
|
||||
}
|
||||
else if ((_param == "TOPIC") || (_param == "MAINTOPIC"))
|
||||
{
|
||||
mqtt_controll_config.maintopic = splitted[1];
|
||||
}
|
||||
else if (_param == "DISCOVERYPREFIX")
|
||||
{
|
||||
mqtt_controll_config.discoveryprefix = splitted[1];
|
||||
}
|
||||
else if (_param == "DOMOTICZTOPICIN")
|
||||
{
|
||||
mqtt_controll_config.domoticzintopic = splitted[1];
|
||||
}
|
||||
else if (_param == "DOMOTICZIDX")
|
||||
{
|
||||
handleIdx(splitted[0], splitted[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((mqtt_controll_config.uri.length() > 0) && (mqtt_controll_config.user.length() > 0))
|
||||
{
|
||||
/* Note:
|
||||
* Originally, we started the MQTT client here.
|
||||
* How ever we need the interval parameter from the ClassFlowControl, but that only gets started later.
|
||||
* To work around this, we delay the start and trigger it from ClassFlowControl::ReadParameter() */
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Init MQTT with uri: " + mqtt_controll_config.uri + ", user: " + mqtt_controll_config.user);
|
||||
|
||||
if (mqtt_controll_config.domoticzintopic.length() > 0)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Init MQTT with domoticzintopic: " + mqtt_controll_config.domoticzintopic);
|
||||
}
|
||||
|
||||
mqtt_controll_config.mqtt_enabled = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "MQTT init skipped as we are missing some parameters");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ClassFlowMQTT::SetMeterType(std::string _meterType, std::string _valueUnit, std::string _timeUnit, std::string _rateUnit)
|
||||
{
|
||||
mqtt_controll_config.meterType = _meterType;
|
||||
mqtt_controll_config.valueUnit = _valueUnit;
|
||||
mqtt_controll_config.timeUnit = _timeUnit;
|
||||
mqtt_controll_config.rateUnit = _rateUnit;
|
||||
}
|
||||
|
||||
bool ClassFlowMQTT::Start(float AutoInterval)
|
||||
{
|
||||
roundInterval = AutoInterval; // Minutes
|
||||
keepAlive = roundInterval * 60 * 2.5; // Seconds, make sure it is greater thatn 2 rounds!
|
||||
mqtt_controll_config.roundInterval = AutoInterval; // Minutes
|
||||
mqtt_controll_config.keepAlive = mqtt_controll_config.roundInterval * 60 * 2.5; // Seconds, make sure it is greater thatn 2 rounds!
|
||||
|
||||
std::stringstream stream;
|
||||
stream << std::fixed << std::setprecision(1) << "Digitizer interval is " << roundInterval <<
|
||||
" minutes => setting MQTT LWT timeout to " << ((float)keepAlive/60) << " minutes.";
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, stream.str());
|
||||
mqttServer_setParameter(flowpostprocessing->GetNumbers());
|
||||
|
||||
mqttServer_setParameter(flowpostprocessing->GetNumbers(), keepAlive, roundInterval);
|
||||
|
||||
bool MQTTConfigCheck = MQTT_Configure(uri, clientname, user, password, maintopic, domoticzintopic, LWT_TOPIC, LWT_CONNECTED,
|
||||
LWT_DISCONNECTED, caCertFilename, validateServerCert, clientCertFilename, clientKeyFilename,
|
||||
keepAlive, SetRetainFlag, (void *)&GotConnected);
|
||||
|
||||
if (!MQTTConfigCheck) {
|
||||
if (!MQTT_Configure((void *)&GotConnected))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return (MQTT_Init() == 1);
|
||||
}
|
||||
|
||||
|
||||
bool ClassFlowMQTT::doFlow(string zwtime)
|
||||
bool ClassFlowMQTT::doFlow(std::string time)
|
||||
{
|
||||
bool success;
|
||||
std::string result;
|
||||
std::string resulterror = "";
|
||||
std::string resultraw = "";
|
||||
std::string resultpre = "";
|
||||
std::string resultrate = ""; // Always Unit / Minute
|
||||
std::string resultRatePerTimeUnit = ""; // According to selection
|
||||
std::string resulttimestamp = "";
|
||||
std::string resultchangabs = "";
|
||||
string zw = "";
|
||||
string namenumber = "";
|
||||
string domoticzpayload = "";
|
||||
string DomoticzIdx = "";
|
||||
int qos = 1;
|
||||
|
||||
std::string value_temp = "";
|
||||
std::string error_message_text_temp = "";
|
||||
std::string raw_value_temp = "";
|
||||
// std::string pre_value_temp = "";
|
||||
std::string rate_value_temp = ""; // Always Unit / Minute
|
||||
std::string time_stamp_temp = "";
|
||||
std::string change_absolute_temp = "";
|
||||
|
||||
/* Send the the Homeassistant Discovery and the Static Topics in case they where scheduled */
|
||||
sendDiscovery_and_static_Topics();
|
||||
|
||||
success = publishSystemData(qos);
|
||||
bool success = publishSystemData(qos);
|
||||
|
||||
if (flowpostprocessing && getMQTTisConnected())
|
||||
{
|
||||
std::vector<NumberPost*>* NUMBERS = flowpostprocessing->GetNumbers();
|
||||
|
||||
std::vector<NumberPost *> *NUMBERS = flowpostprocessing->GetNumbers();
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Publishing MQTT topics...");
|
||||
|
||||
for (int i = 0; i < (*NUMBERS).size(); ++i)
|
||||
{
|
||||
result = (*NUMBERS)[i]->ReturnValue;
|
||||
resultraw = (*NUMBERS)[i]->ReturnRawValue;
|
||||
resultpre = (*NUMBERS)[i]->ReturnPreValue;
|
||||
resulterror = (*NUMBERS)[i]->ErrorMessageText;
|
||||
resultrate = (*NUMBERS)[i]->ReturnRateValue; // Unit per minutes
|
||||
resultchangabs = (*NUMBERS)[i]->ReturnChangeAbsolute; // Units per round
|
||||
resulttimestamp = (*NUMBERS)[i]->timeStamp;
|
||||
value_temp = (*NUMBERS)[i]->ReturnValue;
|
||||
raw_value_temp = (*NUMBERS)[i]->ReturnRawValue;
|
||||
// pre_value_temp = (*NUMBERS)[i]->ReturnPreValue;
|
||||
error_message_text_temp = (*NUMBERS)[i]->ErrorMessageText;
|
||||
rate_value_temp = (*NUMBERS)[i]->ReturnRateValue; // Unit per minutes
|
||||
change_absolute_temp = (*NUMBERS)[i]->ReturnChangeAbsolute; // Units per round
|
||||
time_stamp_temp = (*NUMBERS)[i]->timeStamp;
|
||||
|
||||
DomoticzIdx = (*NUMBERS)[i]->DomoticzIdx;
|
||||
domoticzpayload = "{\"command\":\"udevice\",\"idx\":" + DomoticzIdx + ",\"svalue\":\""+ result + "\"}";
|
||||
std::string DomoticzIdx = (*NUMBERS)[i]->DomoticzIdx;
|
||||
std::string domoticzpayload = "{\"command\":\"udevice\",\"idx\":" + DomoticzIdx + ",\"svalue\":\"" + value_temp + "\"}";
|
||||
|
||||
namenumber = (*NUMBERS)[i]->name;
|
||||
if (namenumber == "default")
|
||||
namenumber = maintopic + "/";
|
||||
std::string name_temp = (*NUMBERS)[i]->name;
|
||||
|
||||
if (name_temp == "default")
|
||||
{
|
||||
name_temp = mqtt_controll_config.maintopic + "/";
|
||||
}
|
||||
else
|
||||
namenumber = maintopic + "/" + namenumber + "/";
|
||||
{
|
||||
name_temp = mqtt_controll_config.maintopic + "/" + name_temp + "/";
|
||||
}
|
||||
|
||||
if ((domoticzintopic.length() > 0) && (result.length() > 0))
|
||||
success |= MQTTPublish(domoticzintopic, domoticzpayload, qos, SetRetainFlag);
|
||||
if ((mqtt_controll_config.domoticzintopic.length() > 0) && (value_temp.length() > 0))
|
||||
{
|
||||
success |= MQTTPublish(mqtt_controll_config.domoticzintopic, domoticzpayload, qos, mqtt_controll_config.retainFlag);
|
||||
}
|
||||
|
||||
if (result.length() > 0)
|
||||
success |= MQTTPublish(namenumber + "value", result, qos, SetRetainFlag);
|
||||
if (resulterror.length() > 0)
|
||||
success |= MQTTPublish(namenumber + "error", resulterror, qos, SetRetainFlag);
|
||||
if (value_temp.length() > 0)
|
||||
{
|
||||
success |= MQTTPublish(name_temp + "value", value_temp, qos, mqtt_controll_config.retainFlag);
|
||||
}
|
||||
|
||||
if (resultrate.length() > 0) {
|
||||
success |= MQTTPublish(namenumber + "rate", resultrate, qos, SetRetainFlag);
|
||||
if (error_message_text_temp.length() > 0)
|
||||
{
|
||||
success |= MQTTPublish(name_temp + "error", error_message_text_temp, qos, mqtt_controll_config.retainFlag);
|
||||
}
|
||||
|
||||
if (rate_value_temp.length() > 0)
|
||||
{
|
||||
success |= MQTTPublish(name_temp + "rate", rate_value_temp, qos, mqtt_controll_config.retainFlag);
|
||||
|
||||
std::string resultRatePerTimeUnit;
|
||||
if (getTimeUnit() == "h") { // Need conversion to be per hour
|
||||
if (mqtt_controll_config.timeUnit == "h")
|
||||
{
|
||||
// Need conversion to be per hour
|
||||
resultRatePerTimeUnit = resultRatePerTimeUnit = to_string((*NUMBERS)[i]->FlowRateAct * 60); // per minutes => per hour
|
||||
}
|
||||
else { // Keep per minute
|
||||
resultRatePerTimeUnit = resultrate;
|
||||
else
|
||||
{
|
||||
// Keep per minute
|
||||
resultRatePerTimeUnit = rate_value_temp;
|
||||
}
|
||||
success |= MQTTPublish(namenumber + "rate_per_time_unit", resultRatePerTimeUnit, qos, SetRetainFlag);
|
||||
success |= MQTTPublish(name_temp + "rate_per_time_unit", resultRatePerTimeUnit, qos, mqtt_controll_config.retainFlag);
|
||||
}
|
||||
|
||||
if (resultchangabs.length() > 0) {
|
||||
success |= MQTTPublish(namenumber + "changeabsolut", resultchangabs, qos, SetRetainFlag); // Legacy API
|
||||
success |= MQTTPublish(namenumber + "rate_per_digitization_round", resultchangabs, qos, SetRetainFlag);
|
||||
if (change_absolute_temp.length() > 0)
|
||||
{
|
||||
success |= MQTTPublish(name_temp + "changeabsolut", change_absolute_temp, qos, mqtt_controll_config.retainFlag); // Legacy API
|
||||
success |= MQTTPublish(name_temp + "rate_per_digitization_round", change_absolute_temp, qos, mqtt_controll_config.retainFlag);
|
||||
}
|
||||
|
||||
if (resultraw.length() > 0)
|
||||
success |= MQTTPublish(namenumber + "raw", resultraw, qos, SetRetainFlag);
|
||||
if (raw_value_temp.length() > 0)
|
||||
{
|
||||
success |= MQTTPublish(name_temp + "raw", raw_value_temp, qos, mqtt_controll_config.retainFlag);
|
||||
}
|
||||
|
||||
if (resulttimestamp.length() > 0)
|
||||
success |= MQTTPublish(namenumber + "timestamp", resulttimestamp, qos, SetRetainFlag);
|
||||
if (time_stamp_temp.length() > 0)
|
||||
{
|
||||
success |= MQTTPublish(name_temp + "timestamp", time_stamp_temp, qos, mqtt_controll_config.retainFlag);
|
||||
}
|
||||
|
||||
std::string json = flowpostprocessing->getJsonFromNumber(i, "\n");
|
||||
success |= MQTTPublish(namenumber + "json", json, qos, SetRetainFlag);
|
||||
success |= MQTTPublish(name_temp + "json", json, qos, mqtt_controll_config.retainFlag);
|
||||
}
|
||||
}
|
||||
|
||||
/* Disabled because this is no longer a use case */
|
||||
// else
|
||||
// {
|
||||
// for (int i = 0; i < ListFlowControll->size(); ++i)
|
||||
// {
|
||||
// zw = (*ListFlowControll)[i]->getReadout();
|
||||
// if (zw.length() > 0)
|
||||
// {
|
||||
// if (result.length() == 0)
|
||||
// result = zw;
|
||||
// else
|
||||
// result = result + "\t" + zw;
|
||||
// }
|
||||
// }
|
||||
// success |= MQTTPublish(topic, result, qos, SetRetainFlag);
|
||||
// }
|
||||
mqtt_controll_config.OldValue = value_temp;
|
||||
|
||||
OldValue = result;
|
||||
|
||||
if (!success) {
|
||||
if (!success)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "One or more MQTT topics failed to be published!");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
void ClassFlowMQTT::handleIdx(string _decsep, string _value)
|
||||
|
||||
void ClassFlowMQTT::handleIdx(std::string _decsep, std::string _value)
|
||||
{
|
||||
string _digit, _decpos;
|
||||
std::string _digit;
|
||||
int _pospunkt = _decsep.find_first_of(".");
|
||||
// ESP_LOGD(TAG, "Name: %s, Pospunkt: %d", _decsep.c_str(), _pospunkt);
|
||||
|
||||
if (_pospunkt > -1)
|
||||
{
|
||||
_digit = _decsep.substr(0, _pospunkt);
|
||||
}
|
||||
else
|
||||
{
|
||||
_digit = "default";
|
||||
}
|
||||
|
||||
for (int j = 0; j < flowpostprocessing->NUMBERS.size(); ++j)
|
||||
{
|
||||
if (_digit == "default") // Set to default first (if nothing else is set)
|
||||
{
|
||||
flowpostprocessing->NUMBERS[j]->DomoticzIdx = _value;
|
||||
}
|
||||
if (flowpostprocessing->NUMBERS[j]->name == _digit)
|
||||
// Set to default first (if nothing else is set)
|
||||
if ((_digit == "default") || (flowpostprocessing->NUMBERS[j]->name == _digit))
|
||||
{
|
||||
flowpostprocessing->NUMBERS[j]->DomoticzIdx = _value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif //ENABLE_MQTT
|
||||
|
||||
@@ -1,43 +1,80 @@
|
||||
#ifdef ENABLE_MQTT
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef CLASSFFLOWMQTT_H
|
||||
#define CLASSFFLOWMQTT_H
|
||||
|
||||
#include "ClassFlow.h"
|
||||
#include <string>
|
||||
#include <mqtt_client.h>
|
||||
|
||||
#include "ClassFlow.h"
|
||||
#include "ClassFlowPostProcessing.h"
|
||||
|
||||
#include <string>
|
||||
typedef struct
|
||||
{
|
||||
bool mqtt_enabled;
|
||||
bool mqtt_configOK;
|
||||
bool mqtt_initialized;
|
||||
bool mqtt_connected;
|
||||
|
||||
class ClassFlowMQTT :
|
||||
public ClassFlow
|
||||
bool HomeAssistantDiscovery;
|
||||
|
||||
esp_mqtt_event_id_t esp_mqtt_ID;
|
||||
|
||||
std::string uri;
|
||||
std::string topic;
|
||||
std::string topicError;
|
||||
std::string clientname;
|
||||
std::string topicRate;
|
||||
std::string topicTimeStamp;
|
||||
std::string topicUptime;
|
||||
std::string topicFreeMem;
|
||||
std::string OldValue;
|
||||
|
||||
std::string user;
|
||||
std::string password;
|
||||
std::string caCertFilename;
|
||||
std::string clientCertFilename;
|
||||
std::string clientKeyFilename;
|
||||
bool validateServerCert;
|
||||
|
||||
std::string maintopic;
|
||||
std::string discoveryprefix;
|
||||
std::string domoticzintopic;
|
||||
|
||||
std::string lwt_topic;
|
||||
std::string lwt_connected;
|
||||
std::string lwt_disconnected;
|
||||
|
||||
std::string meterType;
|
||||
std::string valueUnit;
|
||||
std::string timeUnit;
|
||||
std::string rateUnit;
|
||||
|
||||
float roundInterval; // in Minutes
|
||||
bool retainFlag;
|
||||
int keepAlive; // in Seconds
|
||||
} mqtt_controll_config_t;
|
||||
|
||||
extern mqtt_controll_config_t mqtt_controll_config;
|
||||
|
||||
class ClassFlowMQTT : public ClassFlow
|
||||
{
|
||||
protected:
|
||||
std::string uri, topic, topicError, clientname, topicRate, topicTimeStamp, topicUptime, topicFreeMem;
|
||||
std::string OldValue;
|
||||
ClassFlowPostProcessing* flowpostprocessing;
|
||||
std::string user, password;
|
||||
std::string caCertFilename, clientCertFilename, clientKeyFilename;
|
||||
bool validateServerCert;
|
||||
bool SetRetainFlag;
|
||||
int keepAlive; // Seconds
|
||||
float roundInterval; // Minutes
|
||||
std::string maintopic, domoticzintopic;
|
||||
void SetInitialParameter(void);
|
||||
void handleIdx(string _decsep, string _value);
|
||||
ClassFlowPostProcessing *flowpostprocessing;
|
||||
|
||||
void SetInitialParameter(void);
|
||||
void SetMeterType(std::string _meterType, std::string _valueUnit, std::string _timeUnit, std::string _rateUnit);
|
||||
void handleIdx(std::string _decsep, std::string _value);
|
||||
|
||||
public:
|
||||
ClassFlowMQTT();
|
||||
ClassFlowMQTT(std::vector<ClassFlow*>* lfc);
|
||||
ClassFlowMQTT(std::vector<ClassFlow*>* lfc, ClassFlow *_prev);
|
||||
ClassFlowMQTT(void);
|
||||
ClassFlowMQTT(std::vector<ClassFlow *> *lfc);
|
||||
ClassFlowMQTT(std::vector<ClassFlow *> *lfc, ClassFlow *_prev);
|
||||
|
||||
bool Start(float AutoInterval);
|
||||
|
||||
bool ReadParameter(FILE* pfile, string& aktparamgraph);
|
||||
bool doFlow(string time);
|
||||
string name(){return "ClassFlowMQTT";};
|
||||
bool ReadParameter(FILE *pFile, std::string &aktparamgraph);
|
||||
bool doFlow(std::string time);
|
||||
std::string name(void) { return "ClassFlowMQTT"; };
|
||||
};
|
||||
#endif //CLASSFFLOWMQTT_H
|
||||
#endif //ENABLE_MQTT
|
||||
#endif // CLASSFFLOWMQTT_H
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,15 +3,14 @@
|
||||
#ifndef CLASSFFLOWPOSTPROCESSING_H
|
||||
#define CLASSFFLOWPOSTPROCESSING_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "ClassFlow.h"
|
||||
#include "ClassFlowTakeImage.h"
|
||||
#include "ClassFlowCNNGeneral.h"
|
||||
#include "ClassFlowDefineTypes.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
class ClassFlowPostProcessing :
|
||||
public ClassFlow
|
||||
class ClassFlowPostProcessing : public ClassFlow
|
||||
{
|
||||
protected:
|
||||
bool UpdatePreValueINI;
|
||||
@@ -19,59 +18,60 @@ protected:
|
||||
int PreValueAgeStartup;
|
||||
bool ErrorMessage;
|
||||
|
||||
ClassFlowCNNGeneral* flowAnalog;
|
||||
ClassFlowCNNGeneral* flowDigit;
|
||||
ClassFlowCNNGeneral *flowAnalog;
|
||||
ClassFlowCNNGeneral *flowDigit;
|
||||
|
||||
string FilePreValue;
|
||||
std::string FilePreValue;
|
||||
|
||||
ClassFlowTakeImage *flowTakeImage;
|
||||
|
||||
bool LoadPreValue(void);
|
||||
string ShiftDecimal(string in, int _decShift);
|
||||
std::string ShiftDecimal(std::string in, int _decShift);
|
||||
|
||||
string ErsetzteN(string, double _prevalue);
|
||||
float checkDigitConsistency(double input, int _decilamshift, bool _isanalog, double _preValue);
|
||||
std::string ErsetzteN(std::string, double _prevalue);
|
||||
|
||||
void InitNUMBERS();
|
||||
|
||||
void handleDecimalSeparator(string _decsep, string _value);
|
||||
void handleMaxRateValue(string _decsep, string _value);
|
||||
void handleDecimalExtendedResolution(string _decsep, string _value);
|
||||
void handleMaxRateType(string _decsep, string _value);
|
||||
void handleAnalogToDigitTransitionStart(string _decsep, string _value);
|
||||
void handleAllowNegativeRate(string _decsep, string _value);
|
||||
void handleIgnoreLeadingNaN(string _decsep, string _value);
|
||||
void handleChangeRateThreshold(string _decsep, string _value);
|
||||
void handlecheckDigitIncreaseConsistency(std::string _decsep, std::string _value);
|
||||
void handleDecimalSeparator(std::string _decsep, std::string _value);
|
||||
void handleMaxRateValue(std::string _decsep, std::string _value);
|
||||
void handleDecimalExtendedResolution(std::string _decsep, std::string _value);
|
||||
void handleMaxRateType(std::string _decsep, std::string _value);
|
||||
void handleAnalogToDigitTransitionStart(std::string _decsep, std::string _value);
|
||||
void handleAllowNegativeRate(std::string _decsep, std::string _value);
|
||||
void handleIgnoreLeadingNaN(std::string _decsep, std::string _value);
|
||||
void handleChangeRateThreshold(std::string _decsep, std::string _value);
|
||||
void handleMaxFlowRate(std::string _decsep, std::string _value);
|
||||
|
||||
std::vector<double> addNumbersTogether(std::vector<double> DigitValues, std::vector<double> AnalogValues);
|
||||
|
||||
void WriteDataLog(int _index);
|
||||
|
||||
public:
|
||||
bool PreValueUse;
|
||||
std::vector<NumberPost*> NUMBERS;
|
||||
std::vector<NumberPost *> NUMBERS;
|
||||
|
||||
ClassFlowPostProcessing(std::vector<ClassFlow*>* lfc, ClassFlowCNNGeneral *_analog, ClassFlowCNNGeneral *_digit);
|
||||
virtual ~ClassFlowPostProcessing(){};
|
||||
bool ReadParameter(FILE* pfile, string& aktparamgraph);
|
||||
bool doFlow(string time);
|
||||
string getReadout(int _number);
|
||||
string getReadoutParam(bool _rawValue, bool _noerror, int _number = 0);
|
||||
string getReadoutError(int _number = 0);
|
||||
string getReadoutRate(int _number = 0);
|
||||
string getReadoutTimeStamp(int _number = 0);
|
||||
ClassFlowPostProcessing(std::vector<ClassFlow *> *lfc, ClassFlowCNNGeneral *_analog, ClassFlowCNNGeneral *_digit);
|
||||
virtual ~ClassFlowPostProcessing() {};
|
||||
bool ReadParameter(FILE *pFile, std::string &aktparamgraph);
|
||||
bool doFlow(std::string time);
|
||||
std::string getReadout(int _number);
|
||||
std::string getReadoutParam(bool _rawValue, bool _noerror, int _number = 0);
|
||||
std::string getReadoutError(int _number = 0);
|
||||
std::string getReadoutRate(int _number = 0);
|
||||
std::string getReadoutTimeStamp(int _number = 0);
|
||||
void SavePreValue();
|
||||
string getJsonFromNumber(int i, std::string _lineend);
|
||||
string GetPreValue(std::string _number = "");
|
||||
bool SetPreValue(double zw, string _numbers, bool _extern = false);
|
||||
std::string getJsonFromNumber(int i, std::string _lineend);
|
||||
std::string GetPreValue(std::string _number = "");
|
||||
bool SetPreValue(double _newvalue, string _numbers, bool _extern = false);
|
||||
|
||||
std::string GetJSON(std::string _lineend = "\n");
|
||||
std::string getNumbersName();
|
||||
|
||||
void UpdateNachkommaDecimalShift();
|
||||
|
||||
std::vector<NumberPost*>* GetNumbers(){return &NUMBERS;};
|
||||
std::vector<NumberPost *> *GetNumbers() { return &NUMBERS; };
|
||||
|
||||
string name(){return "ClassFlowPostProcessing";};
|
||||
std::string name() { return "ClassFlowPostProcessing"; };
|
||||
};
|
||||
|
||||
#endif //CLASSFFLOWPOSTPROCESSING_H
|
||||
#endif // CLASSFFLOWPOSTPROCESSING_H
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#include "defines.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -13,20 +15,16 @@
|
||||
|
||||
#include "esp_wifi.h"
|
||||
#include "esp_log.h"
|
||||
#include "../../include/defines.h"
|
||||
#include "psram.h"
|
||||
|
||||
#include <time.h>
|
||||
|
||||
// #define DEBUG_DETAIL_ON
|
||||
// #define WIFITURNOFF
|
||||
|
||||
static const char *TAG = "TAKEIMAGE";
|
||||
|
||||
esp_err_t ClassFlowTakeImage::camera_capture(void)
|
||||
{
|
||||
string nm = namerawimage;
|
||||
Camera.CaptureToFile(nm);
|
||||
std::string file_name = NameRawImage;
|
||||
Camera.capture_to_file(file_name);
|
||||
time(&TimeImageTaken);
|
||||
localtime(&TimeImageTaken);
|
||||
|
||||
@@ -38,17 +36,22 @@ void ClassFlowTakeImage::takePictureWithFlash(int flash_duration)
|
||||
// in case the image is flipped, it must be reset here //
|
||||
rawImage->width = CCstatus.ImageWidth;
|
||||
rawImage->height = CCstatus.ImageHeight;
|
||||
if (Camera.CamTempImage)
|
||||
{
|
||||
rawImage->width = CFstatus.ImageWidth;
|
||||
rawImage->height = CFstatus.ImageHeight;
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "flash_duration: %d", flash_duration);
|
||||
|
||||
Camera.CaptureToBasisImage(rawImage, flash_duration);
|
||||
Camera.capture_to_basis_image(rawImage, flash_duration);
|
||||
|
||||
time(&TimeImageTaken);
|
||||
localtime(&TimeImageTaken);
|
||||
|
||||
if (CCstatus.SaveAllFiles)
|
||||
if (Camera.SaveAllFiles)
|
||||
{
|
||||
rawImage->SaveToFile(namerawimage);
|
||||
rawImage->SaveToFile(NameRawImage);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,466 +59,447 @@ void ClassFlowTakeImage::SetInitialParameter(void)
|
||||
{
|
||||
TimeImageTaken = 0;
|
||||
rawImage = NULL;
|
||||
NameRawImage = "/sdcard/img_tmp/raw.jpg";
|
||||
|
||||
disabled = false;
|
||||
namerawimage = "/sdcard/img_tmp/raw.jpg";
|
||||
}
|
||||
|
||||
// auslesen der Kameraeinstellungen aus der config.ini
|
||||
// wird beim Start aufgerufen
|
||||
bool ClassFlowTakeImage::ReadParameter(FILE *pfile, string &aktparamgraph)
|
||||
bool ClassFlowTakeImage::ReadParameter(FILE *pFile, std::string &aktparamgraph)
|
||||
{
|
||||
Camera.getSensorDatenToCCstatus(); // Kamera >>> CCstatus
|
||||
|
||||
std::vector<string> splitted;
|
||||
|
||||
aktparamgraph = trim(aktparamgraph);
|
||||
|
||||
aktparamgraph = trim_string_left_right(aktparamgraph);
|
||||
if (aktparamgraph.size() == 0)
|
||||
{
|
||||
if (!this->GetNextParagraph(pfile, aktparamgraph))
|
||||
if (!GetNextParagraph(pFile, aktparamgraph))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (aktparamgraph.compare("[TakeImage]") != 0)
|
||||
if ((to_upper(aktparamgraph).compare("[TAKEIMAGE]") != 0) && (to_upper(aktparamgraph).compare(";[TAKEIMAGE]") != 0))
|
||||
{
|
||||
// Paragraph does not fit TakeImage
|
||||
return false;
|
||||
}
|
||||
|
||||
while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph))
|
||||
Camera.get_sensor_controll_config(&CCstatus); // Kamera >>> CCstatus
|
||||
std::vector<std::string> splitted;
|
||||
|
||||
while (getNextLine(pFile, &aktparamgraph) && !isNewParagraph(aktparamgraph))
|
||||
{
|
||||
splitted = ZerlegeZeile(aktparamgraph);
|
||||
splitted = split_line(aktparamgraph);
|
||||
|
||||
if ((toUpper(splitted[0]) == "RAWIMAGESLOCATION") && (splitted.size() > 1))
|
||||
if (splitted.size() > 1)
|
||||
{
|
||||
imagesLocation = "/sdcard" + splitted[1];
|
||||
isLogImage = true;
|
||||
}
|
||||
std::string _param = to_upper(splitted[0]);
|
||||
|
||||
else if ((toUpper(splitted[0]) == "RAWIMAGESRETENTION") && (splitted.size() > 1))
|
||||
{
|
||||
if (isStringNumeric(splitted[1]))
|
||||
if (_param == "RAWIMAGESLOCATION")
|
||||
{
|
||||
this->imagesRetention = std::stod(splitted[1]);
|
||||
imagesLocation = "/sdcard" + splitted[1];
|
||||
isLogImage = true;
|
||||
}
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "SAVEALLFILES") && (splitted.size() > 1))
|
||||
{
|
||||
CCstatus.SaveAllFiles = alphanumericToBoolean(splitted[1]);
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "WAITBEFORETAKINGPICTURE") && (splitted.size() > 1))
|
||||
{
|
||||
if (isStringNumeric(splitted[1]))
|
||||
else if (_param == "RAWIMAGESRETENTION")
|
||||
{
|
||||
int _WaitBeforePicture = std::stoi(splitted[1]);
|
||||
if (_WaitBeforePicture != 0)
|
||||
if (is_string_numeric(splitted[1]))
|
||||
{
|
||||
CCstatus.WaitBeforePicture = _WaitBeforePicture;
|
||||
imagesRetention = std::stod(splitted[1]);
|
||||
}
|
||||
}
|
||||
else if (_param == "SAVEALLFILES")
|
||||
{
|
||||
Camera.SaveAllFiles = alphanumeric_to_boolean(splitted[1]);
|
||||
}
|
||||
else if (_param == "WAITBEFORETAKINGPICTURE")
|
||||
{
|
||||
if (is_string_numeric(splitted[1]))
|
||||
{
|
||||
int _WaitBeforePicture = std::stoi(splitted[1]);
|
||||
if (_WaitBeforePicture != 0)
|
||||
{
|
||||
CCstatus.WaitBeforePicture = _WaitBeforePicture;
|
||||
}
|
||||
else
|
||||
{
|
||||
CCstatus.WaitBeforePicture = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (_param == "CAMXCLKFREQMHZ")
|
||||
{
|
||||
if (is_string_numeric(splitted[1]))
|
||||
{
|
||||
int _CamXclkFreqMhz = std::stoi(splitted[1]);
|
||||
CCstatus.CamXclkFreqMhz = clip_int(_CamXclkFreqMhz, 20, 1);
|
||||
}
|
||||
}
|
||||
else if (_param == "CAMGAINCEILING")
|
||||
{
|
||||
std::string _ImageGainceiling = to_upper(splitted[1]);
|
||||
|
||||
if (is_string_numeric(_ImageGainceiling))
|
||||
{
|
||||
int _ImageGainceiling_ = std::stoi(_ImageGainceiling);
|
||||
switch (_ImageGainceiling_)
|
||||
{
|
||||
case 1:
|
||||
CCstatus.ImageGainceiling = GAINCEILING_4X;
|
||||
break;
|
||||
case 2:
|
||||
CCstatus.ImageGainceiling = GAINCEILING_8X;
|
||||
break;
|
||||
case 3:
|
||||
CCstatus.ImageGainceiling = GAINCEILING_16X;
|
||||
break;
|
||||
case 4:
|
||||
CCstatus.ImageGainceiling = GAINCEILING_32X;
|
||||
break;
|
||||
case 5:
|
||||
CCstatus.ImageGainceiling = GAINCEILING_64X;
|
||||
break;
|
||||
case 6:
|
||||
CCstatus.ImageGainceiling = GAINCEILING_128X;
|
||||
break;
|
||||
default:
|
||||
CCstatus.ImageGainceiling = GAINCEILING_2X;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CCstatus.WaitBeforePicture = 2;
|
||||
if (_ImageGainceiling == "X4")
|
||||
{
|
||||
CCstatus.ImageGainceiling = GAINCEILING_4X;
|
||||
}
|
||||
else if (_ImageGainceiling == "X8")
|
||||
{
|
||||
CCstatus.ImageGainceiling = GAINCEILING_8X;
|
||||
}
|
||||
else if (_ImageGainceiling == "X16")
|
||||
{
|
||||
CCstatus.ImageGainceiling = GAINCEILING_16X;
|
||||
}
|
||||
else if (_ImageGainceiling == "X32")
|
||||
{
|
||||
CCstatus.ImageGainceiling = GAINCEILING_32X;
|
||||
}
|
||||
else if (_ImageGainceiling == "X64")
|
||||
{
|
||||
CCstatus.ImageGainceiling = GAINCEILING_64X;
|
||||
}
|
||||
else if (_ImageGainceiling == "X128")
|
||||
{
|
||||
CCstatus.ImageGainceiling = GAINCEILING_128X;
|
||||
}
|
||||
else
|
||||
{
|
||||
CCstatus.ImageGainceiling = GAINCEILING_2X;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "CAMGAINCEILING") && (splitted.size() > 1))
|
||||
{
|
||||
std::string _ImageGainceiling = toUpper(splitted[1]);
|
||||
|
||||
if (isStringNumeric(_ImageGainceiling))
|
||||
else if (_param == "CAMQUALITY")
|
||||
{
|
||||
int _ImageGainceiling_ = std::stoi(_ImageGainceiling);
|
||||
switch (_ImageGainceiling_)
|
||||
if (is_string_numeric(splitted[1]))
|
||||
{
|
||||
case 1:
|
||||
CCstatus.ImageGainceiling = GAINCEILING_4X;
|
||||
break;
|
||||
case 2:
|
||||
CCstatus.ImageGainceiling = GAINCEILING_8X;
|
||||
break;
|
||||
case 3:
|
||||
CCstatus.ImageGainceiling = GAINCEILING_16X;
|
||||
break;
|
||||
case 4:
|
||||
CCstatus.ImageGainceiling = GAINCEILING_32X;
|
||||
break;
|
||||
case 5:
|
||||
CCstatus.ImageGainceiling = GAINCEILING_64X;
|
||||
break;
|
||||
case 6:
|
||||
CCstatus.ImageGainceiling = GAINCEILING_128X;
|
||||
break;
|
||||
default:
|
||||
CCstatus.ImageGainceiling = GAINCEILING_2X;
|
||||
int _ImageQuality = std::stoi(splitted[1]);
|
||||
CCstatus.ImageQuality = clip_int(_ImageQuality, 63, 6);
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (_param == "CAMBRIGHTNESS")
|
||||
{
|
||||
if (_ImageGainceiling == "X4")
|
||||
if (is_string_numeric(splitted[1]))
|
||||
{
|
||||
CCstatus.ImageGainceiling = GAINCEILING_4X;
|
||||
int _ImageBrightness = std::stoi(splitted[1]);
|
||||
CCstatus.ImageBrightness = clip_int(_ImageBrightness, 2, -2);
|
||||
}
|
||||
else if (_ImageGainceiling == "X8")
|
||||
}
|
||||
else if (_param == "CAMCONTRAST")
|
||||
{
|
||||
if (is_string_numeric(splitted[1]))
|
||||
{
|
||||
CCstatus.ImageGainceiling = GAINCEILING_8X;
|
||||
int _ImageContrast = std::stoi(splitted[1]);
|
||||
CCstatus.ImageContrast = clip_int(_ImageContrast, 2, -2);
|
||||
}
|
||||
else if (_ImageGainceiling == "X16")
|
||||
}
|
||||
else if (_param == "CAMSATURATION")
|
||||
{
|
||||
if (is_string_numeric(splitted[1]))
|
||||
{
|
||||
CCstatus.ImageGainceiling = GAINCEILING_16X;
|
||||
int _ImageSaturation = std::stoi(splitted[1]);
|
||||
CCstatus.ImageSaturation = clip_int(_ImageSaturation, 2, -2);
|
||||
}
|
||||
else if (_ImageGainceiling == "X32")
|
||||
}
|
||||
else if (_param == "CAMSHARPNESS")
|
||||
{
|
||||
if (is_string_numeric(splitted[1]))
|
||||
{
|
||||
CCstatus.ImageGainceiling = GAINCEILING_32X;
|
||||
int _ImageSharpness = std::stoi(splitted[1]);
|
||||
if (Camera.CamSensorId == OV2640_PID)
|
||||
{
|
||||
CCstatus.ImageSharpness = clip_int(_ImageSharpness, 2, -2);
|
||||
}
|
||||
else
|
||||
{
|
||||
CCstatus.ImageSharpness = clip_int(_ImageSharpness, 3, -3);
|
||||
}
|
||||
}
|
||||
else if (_ImageGainceiling == "X64")
|
||||
}
|
||||
else if (_param == "CAMAUTOSHARPNESS")
|
||||
{
|
||||
CCstatus.ImageAutoSharpness = alphanumeric_to_boolean(splitted[1]);
|
||||
}
|
||||
else if (_param == "CAMSPECIALEFFECT")
|
||||
{
|
||||
std::string _ImageSpecialEffect = to_upper(splitted[1]);
|
||||
|
||||
if (is_string_numeric(_ImageSpecialEffect))
|
||||
{
|
||||
CCstatus.ImageGainceiling = GAINCEILING_64X;
|
||||
}
|
||||
else if (_ImageGainceiling == "X128")
|
||||
{
|
||||
CCstatus.ImageGainceiling = GAINCEILING_128X;
|
||||
int _ImageSpecialEffect_ = std::stoi(_ImageSpecialEffect);
|
||||
CCstatus.ImageSpecialEffect = clip_int(_ImageSpecialEffect_, 6, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
CCstatus.ImageGainceiling = GAINCEILING_2X;
|
||||
if (_ImageSpecialEffect == "NEGATIVE")
|
||||
{
|
||||
CCstatus.ImageSpecialEffect = 1;
|
||||
}
|
||||
else if (_ImageSpecialEffect == "GRAYSCALE")
|
||||
{
|
||||
CCstatus.ImageSpecialEffect = 2;
|
||||
}
|
||||
else if (_ImageSpecialEffect == "RED")
|
||||
{
|
||||
CCstatus.ImageSpecialEffect = 3;
|
||||
}
|
||||
else if (_ImageSpecialEffect == "GREEN")
|
||||
{
|
||||
CCstatus.ImageSpecialEffect = 4;
|
||||
}
|
||||
else if (_ImageSpecialEffect == "BLUE")
|
||||
{
|
||||
CCstatus.ImageSpecialEffect = 5;
|
||||
}
|
||||
else if (_ImageSpecialEffect == "RETRO")
|
||||
{
|
||||
CCstatus.ImageSpecialEffect = 6;
|
||||
}
|
||||
else
|
||||
{
|
||||
CCstatus.ImageSpecialEffect = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "CAMQUALITY") && (splitted.size() > 1))
|
||||
{
|
||||
if (isStringNumeric(splitted[1]))
|
||||
else if (_param == "CAMWBMODE")
|
||||
{
|
||||
int _ImageQuality = std::stoi(splitted[1]);
|
||||
CCstatus.ImageQuality = clipInt(_ImageQuality, 63, 6);
|
||||
}
|
||||
}
|
||||
std::string _ImageWbMode = to_upper(splitted[1]);
|
||||
|
||||
else if ((toUpper(splitted[0]) == "CAMBRIGHTNESS") && (splitted.size() > 1))
|
||||
{
|
||||
if (isStringNumeric(splitted[1]))
|
||||
{
|
||||
int _ImageBrightness = std::stoi(splitted[1]);
|
||||
CCstatus.ImageBrightness = clipInt(_ImageBrightness, 2, -2);
|
||||
}
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "CAMCONTRAST") && (splitted.size() > 1))
|
||||
{
|
||||
if (isStringNumeric(splitted[1]))
|
||||
{
|
||||
int _ImageContrast = std::stoi(splitted[1]);
|
||||
CCstatus.ImageContrast = clipInt(_ImageContrast, 2, -2);
|
||||
}
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "CAMSATURATION") && (splitted.size() > 1))
|
||||
{
|
||||
if (isStringNumeric(splitted[1]))
|
||||
{
|
||||
int _ImageSaturation = std::stoi(splitted[1]);
|
||||
CCstatus.ImageSaturation = clipInt(_ImageSaturation, 2, -2);
|
||||
}
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "CAMSHARPNESS") && (splitted.size() > 1))
|
||||
{
|
||||
if (isStringNumeric(splitted[1]))
|
||||
{
|
||||
int _ImageSharpness = std::stoi(splitted[1]);
|
||||
if (CCstatus.CamSensor_id == OV2640_PID)
|
||||
if (is_string_numeric(_ImageWbMode))
|
||||
{
|
||||
CCstatus.ImageSharpness = clipInt(_ImageSharpness, 2, -2);
|
||||
int _ImageWbMode_ = std::stoi(_ImageWbMode);
|
||||
CCstatus.ImageWbMode = clip_int(_ImageWbMode_, 4, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
CCstatus.ImageSharpness = clipInt(_ImageSharpness, 3, -3);
|
||||
if (_ImageWbMode == "SUNNY")
|
||||
{
|
||||
CCstatus.ImageWbMode = 1;
|
||||
}
|
||||
else if (_ImageWbMode == "CLOUDY")
|
||||
{
|
||||
CCstatus.ImageWbMode = 2;
|
||||
}
|
||||
else if (_ImageWbMode == "OFFICE")
|
||||
{
|
||||
CCstatus.ImageWbMode = 3;
|
||||
}
|
||||
else if (_ImageWbMode == "HOME")
|
||||
{
|
||||
CCstatus.ImageWbMode = 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
CCstatus.ImageWbMode = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "CAMAUTOSHARPNESS") && (splitted.size() > 1))
|
||||
{
|
||||
CCstatus.ImageAutoSharpness = alphanumericToBoolean(splitted[1]);
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "CAMSPECIALEFFECT") && (splitted.size() > 1))
|
||||
{
|
||||
std::string _ImageSpecialEffect = toUpper(splitted[1]);
|
||||
|
||||
if (isStringNumeric(_ImageSpecialEffect))
|
||||
else if (_param == "CAMAWB")
|
||||
{
|
||||
int _ImageSpecialEffect_ = std::stoi(_ImageSpecialEffect);
|
||||
CCstatus.ImageSpecialEffect = clipInt(_ImageSpecialEffect_, 6, 0);
|
||||
CCstatus.ImageAwb = alphanumeric_to_boolean(splitted[1]);
|
||||
}
|
||||
else
|
||||
else if (_param == "CAMAWBGAIN")
|
||||
{
|
||||
if (_ImageSpecialEffect == "NEGATIVE")
|
||||
CCstatus.ImageAwbGain = alphanumeric_to_boolean(splitted[1]);
|
||||
}
|
||||
else if (_param == "CAMAEC")
|
||||
{
|
||||
CCstatus.ImageAec = alphanumeric_to_boolean(splitted[1]);
|
||||
}
|
||||
else if (_param == "CAMAEC2")
|
||||
{
|
||||
CCstatus.ImageAec2 = alphanumeric_to_boolean(splitted[1]);
|
||||
}
|
||||
else if (_param == "CAMAELEVEL")
|
||||
{
|
||||
if (is_string_numeric(splitted[1]))
|
||||
{
|
||||
CCstatus.ImageSpecialEffect = 1;
|
||||
}
|
||||
else if (_ImageSpecialEffect == "GRAYSCALE")
|
||||
{
|
||||
CCstatus.ImageSpecialEffect = 2;
|
||||
}
|
||||
else if (_ImageSpecialEffect == "RED")
|
||||
{
|
||||
CCstatus.ImageSpecialEffect = 3;
|
||||
}
|
||||
else if (_ImageSpecialEffect == "GREEN")
|
||||
{
|
||||
CCstatus.ImageSpecialEffect = 4;
|
||||
}
|
||||
else if (_ImageSpecialEffect == "BLUE")
|
||||
{
|
||||
CCstatus.ImageSpecialEffect = 5;
|
||||
}
|
||||
else if (_ImageSpecialEffect == "RETRO")
|
||||
{
|
||||
CCstatus.ImageSpecialEffect = 6;
|
||||
}
|
||||
else
|
||||
{
|
||||
CCstatus.ImageSpecialEffect = 0;
|
||||
int _ImageAeLevel = std::stoi(splitted[1]);
|
||||
if (Camera.CamSensorId == OV2640_PID)
|
||||
{
|
||||
CCstatus.ImageAeLevel = clip_int(_ImageAeLevel, 2, -2);
|
||||
}
|
||||
else
|
||||
{
|
||||
CCstatus.ImageAeLevel = clip_int(_ImageAeLevel, 5, -5);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "CAMWBMODE") && (splitted.size() > 1))
|
||||
{
|
||||
std::string _ImageWbMode = toUpper(splitted[1]);
|
||||
|
||||
if (isStringNumeric(_ImageWbMode))
|
||||
else if (_param == "CAMAECVALUE")
|
||||
{
|
||||
int _ImageWbMode_ = std::stoi(_ImageWbMode);
|
||||
CCstatus.ImageWbMode = clipInt(_ImageWbMode_, 4, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_ImageWbMode == "SUNNY")
|
||||
if (is_string_numeric(splitted[1]))
|
||||
{
|
||||
CCstatus.ImageWbMode = 1;
|
||||
}
|
||||
else if (_ImageWbMode == "CLOUDY")
|
||||
{
|
||||
CCstatus.ImageWbMode = 2;
|
||||
}
|
||||
else if (_ImageWbMode == "OFFICE")
|
||||
{
|
||||
CCstatus.ImageWbMode = 3;
|
||||
}
|
||||
else if (_ImageWbMode == "HOME")
|
||||
{
|
||||
CCstatus.ImageWbMode = 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
CCstatus.ImageWbMode = 0;
|
||||
int _ImageAecValue = std::stoi(splitted[1]);
|
||||
CCstatus.ImageAecValue = clip_int(_ImageAecValue, 1200, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "CAMAWB") && (splitted.size() > 1))
|
||||
{
|
||||
CCstatus.ImageAwb = alphanumericToBoolean(splitted[1]);
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "CAMAWBGAIN") && (splitted.size() > 1))
|
||||
{
|
||||
CCstatus.ImageAwbGain = alphanumericToBoolean(splitted[1]);
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "CAMAEC") && (splitted.size() > 1))
|
||||
{
|
||||
CCstatus.ImageAec = alphanumericToBoolean(splitted[1]);
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "CAMAEC2") && (splitted.size() > 1))
|
||||
{
|
||||
CCstatus.ImageAec2 = alphanumericToBoolean(splitted[1]);
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "CAMAELEVEL") && (splitted.size() > 1))
|
||||
{
|
||||
if (isStringNumeric(splitted[1]))
|
||||
else if (_param == "CAMAGC")
|
||||
{
|
||||
int _ImageAeLevel = std::stoi(splitted[1]);
|
||||
if (CCstatus.CamSensor_id == OV2640_PID)
|
||||
CCstatus.ImageAgc = alphanumeric_to_boolean(splitted[1]);
|
||||
}
|
||||
else if (_param == "CAMAGCGAIN")
|
||||
{
|
||||
if (is_string_numeric(splitted[1]))
|
||||
{
|
||||
CCstatus.ImageAeLevel = clipInt(_ImageAeLevel, 2, -2);
|
||||
}
|
||||
else
|
||||
{
|
||||
CCstatus.ImageAeLevel = clipInt(_ImageAeLevel, 5, -5);
|
||||
int _ImageAgcGain = std::stoi(splitted[1]);
|
||||
CCstatus.ImageAgcGain = clip_int(_ImageAgcGain, 30, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "CAMAECVALUE") && (splitted.size() > 1))
|
||||
{
|
||||
if (isStringNumeric(splitted[1]))
|
||||
else if (_param == "CAMBPC")
|
||||
{
|
||||
int _ImageAecValue = std::stoi(splitted[1]);
|
||||
CCstatus.ImageAecValue = clipInt(_ImageAecValue, 1200, 0);
|
||||
CCstatus.ImageBpc = alphanumeric_to_boolean(splitted[1]);
|
||||
}
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "CAMAGC") && (splitted.size() > 1))
|
||||
{
|
||||
CCstatus.ImageAgc = alphanumericToBoolean(splitted[1]);
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "CAMAGCGAIN") && (splitted.size() > 1))
|
||||
{
|
||||
if (isStringNumeric(splitted[1]))
|
||||
else if (_param == "CAMWPC")
|
||||
{
|
||||
int _ImageAgcGain = std::stoi(splitted[1]);
|
||||
CCstatus.ImageAgcGain = clipInt(_ImageAgcGain, 30, 0);
|
||||
CCstatus.ImageWpc = alphanumeric_to_boolean(splitted[1]);
|
||||
}
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "CAMBPC") && (splitted.size() > 1))
|
||||
{
|
||||
CCstatus.ImageBpc = alphanumericToBoolean(splitted[1]);
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "CAMWPC") && (splitted.size() > 1))
|
||||
{
|
||||
CCstatus.ImageWpc = alphanumericToBoolean(splitted[1]);
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "CAMRAWGMA") && (splitted.size() > 1))
|
||||
{
|
||||
CCstatus.ImageRawGma = alphanumericToBoolean(splitted[1]);
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "CAMLENC") && (splitted.size() > 1))
|
||||
{
|
||||
CCstatus.ImageLenc = alphanumericToBoolean(splitted[1]);
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "CAMHMIRROR") && (splitted.size() > 1))
|
||||
{
|
||||
CCstatus.ImageHmirror = alphanumericToBoolean(splitted[1]);
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "CAMVFLIP") && (splitted.size() > 1))
|
||||
{
|
||||
CCstatus.ImageVflip = alphanumericToBoolean(splitted[1]);
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "CAMDCW") && (splitted.size() > 1))
|
||||
{
|
||||
CCstatus.ImageDcw = alphanumericToBoolean(splitted[1]);
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "CAMDENOISE") && (splitted.size() > 1))
|
||||
{
|
||||
if (isStringNumeric(splitted[1]))
|
||||
else if (_param == "CAMRAWGMA")
|
||||
{
|
||||
int _ImageDenoiseLevel = std::stoi(splitted[1]);
|
||||
if (CCstatus.CamSensor_id == OV2640_PID)
|
||||
CCstatus.ImageRawGma = alphanumeric_to_boolean(splitted[1]);
|
||||
}
|
||||
else if (_param == "CAMLENC")
|
||||
{
|
||||
CCstatus.ImageLenc = alphanumeric_to_boolean(splitted[1]);
|
||||
}
|
||||
else if (_param == "CAMHMIRROR")
|
||||
{
|
||||
CCstatus.ImageHmirror = alphanumeric_to_boolean(splitted[1]);
|
||||
}
|
||||
else if (_param == "CAMVFLIP")
|
||||
{
|
||||
CCstatus.ImageVflip = alphanumeric_to_boolean(splitted[1]);
|
||||
}
|
||||
else if (_param == "CAMDCW")
|
||||
{
|
||||
CCstatus.ImageDcw = alphanumeric_to_boolean(splitted[1]);
|
||||
}
|
||||
else if (_param == "CAMDENOISE")
|
||||
{
|
||||
if (is_string_numeric(splitted[1]))
|
||||
{
|
||||
CCstatus.ImageDenoiseLevel = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
CCstatus.ImageDenoiseLevel = clipInt(_ImageDenoiseLevel, 8, 0);
|
||||
int _ImageDenoiseLevel = std::stoi(splitted[1]);
|
||||
if (Camera.CamSensorId == OV2640_PID)
|
||||
{
|
||||
CCstatus.ImageDenoiseLevel = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
CCstatus.ImageDenoiseLevel = clip_int(_ImageDenoiseLevel, 8, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "CAMZOOM") && (splitted.size() > 1))
|
||||
{
|
||||
CCstatus.ImageZoomEnabled = alphanumericToBoolean(splitted[1]);
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "CAMZOOMOFFSETX") && (splitted.size() > 1))
|
||||
{
|
||||
if (isStringNumeric(splitted[1]))
|
||||
else if (_param == "CAMZOOM")
|
||||
{
|
||||
int _ImageZoomOffsetX = std::stoi(splitted[1]);
|
||||
if (CCstatus.CamSensor_id == OV2640_PID)
|
||||
CCstatus.ImageZoomEnabled = alphanumeric_to_boolean(splitted[1]);
|
||||
}
|
||||
else if (_param == "CAMZOOMOFFSETX")
|
||||
{
|
||||
if (is_string_numeric(splitted[1]))
|
||||
{
|
||||
CCstatus.ImageZoomOffsetX = clipInt(_ImageZoomOffsetX, 480, -480);
|
||||
}
|
||||
else if (CCstatus.CamSensor_id == OV3660_PID)
|
||||
{
|
||||
CCstatus.ImageZoomOffsetX = clipInt(_ImageZoomOffsetX, 704, -704);
|
||||
}
|
||||
else if (CCstatus.CamSensor_id == OV5640_PID)
|
||||
{
|
||||
CCstatus.ImageZoomOffsetX = clipInt(_ImageZoomOffsetX, 960, -960);
|
||||
int _ImageZoomOffsetX = std::stoi(splitted[1]);
|
||||
if (Camera.CamSensorId == OV2640_PID)
|
||||
{
|
||||
CCstatus.ImageZoomOffsetX = clip_int(_ImageZoomOffsetX, 480, -480);
|
||||
}
|
||||
else if (Camera.CamSensorId == OV3660_PID)
|
||||
{
|
||||
CCstatus.ImageZoomOffsetX = clip_int(_ImageZoomOffsetX, 704, -704);
|
||||
}
|
||||
else if (Camera.CamSensorId == OV5640_PID)
|
||||
{
|
||||
CCstatus.ImageZoomOffsetX = clip_int(_ImageZoomOffsetX, 960, -960);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "CAMZOOMOFFSETY") && (splitted.size() > 1))
|
||||
{
|
||||
if (isStringNumeric(splitted[1]))
|
||||
else if (_param == "CAMZOOMOFFSETY")
|
||||
{
|
||||
int _ImageZoomOffsetY = std::stoi(splitted[1]);
|
||||
if (CCstatus.CamSensor_id == OV2640_PID)
|
||||
if (is_string_numeric(splitted[1]))
|
||||
{
|
||||
CCstatus.ImageZoomOffsetY = clipInt(_ImageZoomOffsetY, 360, -360);
|
||||
}
|
||||
else if (CCstatus.CamSensor_id == OV3660_PID)
|
||||
{
|
||||
CCstatus.ImageZoomOffsetY = clipInt(_ImageZoomOffsetY, 528, -528);
|
||||
}
|
||||
else if (CCstatus.CamSensor_id == OV5640_PID)
|
||||
{
|
||||
CCstatus.ImageZoomOffsetY = clipInt(_ImageZoomOffsetY, 720, -720);
|
||||
int _ImageZoomOffsetY = std::stoi(splitted[1]);
|
||||
if (Camera.CamSensorId == OV2640_PID)
|
||||
{
|
||||
CCstatus.ImageZoomOffsetY = clip_int(_ImageZoomOffsetY, 360, -360);
|
||||
}
|
||||
else if (Camera.CamSensorId == OV3660_PID)
|
||||
{
|
||||
CCstatus.ImageZoomOffsetY = clip_int(_ImageZoomOffsetY, 528, -528);
|
||||
}
|
||||
else if (Camera.CamSensorId == OV5640_PID)
|
||||
{
|
||||
CCstatus.ImageZoomOffsetY = clip_int(_ImageZoomOffsetY, 720, -720);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "CAMZOOMSIZE") && (splitted.size() > 1))
|
||||
{
|
||||
if (isStringNumeric(splitted[1]))
|
||||
else if (_param == "CAMZOOMSIZE")
|
||||
{
|
||||
int _ImageZoomSize = std::stoi(splitted[1]);
|
||||
if (CCstatus.CamSensor_id == OV2640_PID)
|
||||
if (is_string_numeric(splitted[1]))
|
||||
{
|
||||
CCstatus.ImageZoomSize = clipInt(_ImageZoomSize, 29, 0);
|
||||
}
|
||||
else if (CCstatus.CamSensor_id == OV3660_PID)
|
||||
{
|
||||
CCstatus.ImageZoomSize = clipInt(_ImageZoomSize, 43, 0);
|
||||
}
|
||||
else if (CCstatus.CamSensor_id == OV5640_PID)
|
||||
{
|
||||
CCstatus.ImageZoomSize = clipInt(_ImageZoomSize, 59, 0);
|
||||
int _ImageZoomSize = std::stoi(splitted[1]);
|
||||
if (Camera.CamSensorId == OV2640_PID)
|
||||
{
|
||||
CCstatus.ImageZoomSize = clip_int(_ImageZoomSize, 29, 0);
|
||||
}
|
||||
else if (Camera.CamSensorId == OV3660_PID)
|
||||
{
|
||||
CCstatus.ImageZoomSize = clip_int(_ImageZoomSize, 43, 0);
|
||||
}
|
||||
else if (Camera.CamSensorId == OV5640_PID)
|
||||
{
|
||||
CCstatus.ImageZoomSize = clip_int(_ImageZoomSize, 59, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "LEDINTENSITY") && (splitted.size() > 1))
|
||||
{
|
||||
if (isStringNumeric(splitted[1]))
|
||||
else if (_param == "LEDINTENSITY")
|
||||
{
|
||||
int ledintensity = std::stoi(splitted[1]);
|
||||
CCstatus.ImageLedIntensity = Camera.SetLEDIntensity(ledintensity);
|
||||
if (is_string_numeric(splitted[1]))
|
||||
{
|
||||
int ledintensity = std::stoi(splitted[1]);
|
||||
CCstatus.ImageLedIntensity = Camera.set_led_intensity(ledintensity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if ((toUpper(splitted[0]) == "DEMO") && (splitted.size() > 1))
|
||||
{
|
||||
CCstatus.DemoMode = alphanumericToBoolean(splitted[1]);
|
||||
if (CCstatus.DemoMode == true)
|
||||
else if (_param == "DEMO")
|
||||
{
|
||||
Camera.useDemoMode();
|
||||
Camera.DemoMode = alphanumeric_to_boolean(splitted[1]);
|
||||
if (Camera.DemoMode == true)
|
||||
{
|
||||
Camera.use_demo_mode();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Camera.setSensorDatenFromCCstatus(); // CCstatus >>> Kamera
|
||||
Camera.SetQualityZoomSize(CCstatus.ImageQuality, CCstatus.ImageFrameSize, CCstatus.ImageZoomEnabled, CCstatus.ImageZoomOffsetX, CCstatus.ImageZoomOffsetY, CCstatus.ImageZoomSize, CCstatus.ImageVflip);
|
||||
Camera.set_sensor_controll_config(&CCstatus); // CCstatus >>> Kamera
|
||||
Camera.set_quality_zoom_size(&CCstatus);
|
||||
|
||||
Camera.changedCameraSettings = false;
|
||||
Camera.CamTempImage = false;
|
||||
|
||||
rawImage = new CImageBasis("rawImage");
|
||||
rawImage->CreateEmptyImage(CCstatus.ImageWidth, CCstatus.ImageHeight, 3);
|
||||
@@ -530,57 +514,39 @@ ClassFlowTakeImage::ClassFlowTakeImage(std::vector<ClassFlow *> *lfc) : ClassFlo
|
||||
SetInitialParameter();
|
||||
}
|
||||
|
||||
string ClassFlowTakeImage::getHTMLSingleStep(string host)
|
||||
std::string ClassFlowTakeImage::getHTMLSingleStep(std::string host)
|
||||
{
|
||||
string result;
|
||||
std::string result;
|
||||
result = "Raw Image: <br>\n<img src=\"" + host + "/img_tmp/raw.jpg\">\n";
|
||||
return result;
|
||||
}
|
||||
|
||||
// wird bei jeder Auswertrunde aufgerufen
|
||||
bool ClassFlowTakeImage::doFlow(string zwtime)
|
||||
bool ClassFlowTakeImage::doFlow(std::string zwtime)
|
||||
{
|
||||
psram_init_shared_memory_for_take_image_step();
|
||||
|
||||
string logPath = CreateLogFolder(zwtime);
|
||||
std::string logPath = CreateLogFolder(zwtime);
|
||||
|
||||
int flash_duration = (int)(CCstatus.WaitBeforePicture * 1000);
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("ClassFlowTakeImage::doFlow - Before takePictureWithFlash");
|
||||
#endif
|
||||
if (Camera.CamTempImage)
|
||||
{
|
||||
flash_duration = (int)(CFstatus.WaitBeforePicture * 1000);
|
||||
}
|
||||
|
||||
#ifdef WIFITURNOFF
|
||||
esp_wifi_stop(); // to save power usage and
|
||||
#endif
|
||||
|
||||
// wenn die Kameraeinstellungen durch Erstellen eines neuen Referenzbildes verändert wurden, müssen sie neu gesetzt werden
|
||||
if (CFstatus.changedCameraSettings)
|
||||
{
|
||||
Camera.setSensorDatenFromCCstatus(); // CCstatus >>> Kamera
|
||||
Camera.SetQualityZoomSize(CCstatus.ImageQuality, CCstatus.ImageFrameSize, CCstatus.ImageZoomEnabled, CCstatus.ImageZoomOffsetX, CCstatus.ImageZoomOffsetY, CCstatus.ImageZoomSize, CCstatus.ImageVflip);
|
||||
Camera.LedIntensity = CCstatus.ImageLedIntensity;
|
||||
CFstatus.changedCameraSettings = false;
|
||||
}
|
||||
|
||||
takePictureWithFlash(flash_duration);
|
||||
|
||||
#ifdef WIFITURNOFF
|
||||
esp_wifi_start();
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("ClassFlowTakeImage::doFlow - After takePictureWithFlash");
|
||||
#endif
|
||||
|
||||
LogImage(logPath, "raw", NULL, NULL, zwtime, rawImage);
|
||||
|
||||
RemoveOldLogs();
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("ClassFlowTakeImage::doFlow - After RemoveOldLogs");
|
||||
#endif
|
||||
|
||||
psram_deinit_shared_memory_for_take_image_step();
|
||||
|
||||
return true;
|
||||
@@ -589,18 +555,29 @@ bool ClassFlowTakeImage::doFlow(string zwtime)
|
||||
esp_err_t ClassFlowTakeImage::SendRawJPG(httpd_req_t *req)
|
||||
{
|
||||
int flash_duration = (int)(CCstatus.WaitBeforePicture * 1000);
|
||||
if (Camera.CamTempImage)
|
||||
{
|
||||
flash_duration = (int)(CFstatus.WaitBeforePicture * 1000);
|
||||
}
|
||||
|
||||
time(&TimeImageTaken);
|
||||
localtime(&TimeImageTaken);
|
||||
|
||||
return Camera.CaptureToHTTP(req, flash_duration);
|
||||
return Camera.capture_to_http(req, flash_duration);
|
||||
}
|
||||
|
||||
ImageData *ClassFlowTakeImage::SendRawImage(void)
|
||||
{
|
||||
CImageBasis *zw = new CImageBasis("SendRawImage", rawImage);
|
||||
ImageData *id;
|
||||
|
||||
int flash_duration = (int)(CCstatus.WaitBeforePicture * 1000);
|
||||
Camera.CaptureToBasisImage(zw, flash_duration);
|
||||
if (Camera.CamTempImage)
|
||||
{
|
||||
flash_duration = (int)(CFstatus.WaitBeforePicture * 1000);
|
||||
}
|
||||
|
||||
Camera.capture_to_basis_image(zw, flash_duration);
|
||||
time(&TimeImageTaken);
|
||||
localtime(&TimeImageTaken);
|
||||
|
||||
|
||||
@@ -3,17 +3,17 @@
|
||||
#ifndef CLASSFFLOWTAKEIMAGE_H
|
||||
#define CLASSFFLOWTAKEIMAGE_H
|
||||
|
||||
#include <string>
|
||||
#include "defines.h"
|
||||
|
||||
#include "ClassFlowImage.h"
|
||||
#include "ClassControllCamera.h"
|
||||
#include "../../include/defines.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
class ClassFlowTakeImage : public ClassFlowImage
|
||||
{
|
||||
protected:
|
||||
time_t TimeImageTaken;
|
||||
string namerawimage;
|
||||
std::string NameRawImage;
|
||||
|
||||
esp_err_t camera_capture(void);
|
||||
void takePictureWithFlash(int flash_duration);
|
||||
@@ -25,11 +25,11 @@ public:
|
||||
|
||||
ClassFlowTakeImage(std::vector<ClassFlow *> *lfc);
|
||||
|
||||
bool ReadParameter(FILE *pfile, string &aktparamgraph);
|
||||
bool doFlow(string time);
|
||||
string getHTMLSingleStep(string host);
|
||||
bool ReadParameter(FILE *pFile, std::string &aktparamgraph);
|
||||
bool doFlow(std::string time);
|
||||
std::string getHTMLSingleStep(std::string host);
|
||||
time_t getTimeImageTaken(void);
|
||||
string name() { return "ClassFlowTakeImage"; };
|
||||
std::string name() { return "ClassFlowTakeImage"; };
|
||||
|
||||
ImageData *SendRawImage(void);
|
||||
esp_err_t SendRawJPG(httpd_req_t *req);
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
#ifdef ENABLE_WEBHOOK
|
||||
#include <sstream>
|
||||
#include "ClassFlowWebhook.h"
|
||||
#include "defines.h"
|
||||
#include "Helper.h"
|
||||
#include "connect_wlan.h"
|
||||
|
||||
#include <time.h>
|
||||
#include <sstream>
|
||||
|
||||
#include "ClassFlowWebhook.h"
|
||||
#include "connect_wifi_sta.h"
|
||||
|
||||
#include "time_sntp.h"
|
||||
#include "interface_webhook.h"
|
||||
@@ -10,24 +13,27 @@
|
||||
#include "ClassFlowPostProcessing.h"
|
||||
#include "ClassFlowAlignment.h"
|
||||
#include "esp_log.h"
|
||||
#include "../../include/defines.h"
|
||||
|
||||
#include "ClassLogFile.h"
|
||||
|
||||
#include <time.h>
|
||||
static const char *TAG = "WEBHOOK";
|
||||
|
||||
static const char* TAG = "WEBHOOK";
|
||||
Webhook_controll_config_t Webhook_controll_config;
|
||||
|
||||
void ClassFlowWebhook::SetInitialParameter(void)
|
||||
{
|
||||
uri = "";
|
||||
Webhook_controll_config.enabled = false;
|
||||
Webhook_controll_config.uri = "";
|
||||
Webhook_controll_config.apikey = "";
|
||||
Webhook_controll_config.uploadImg = 0;
|
||||
Webhook_controll_config.oldValue = "";
|
||||
|
||||
flowpostprocessing = NULL;
|
||||
flowAlignment = NULL;
|
||||
previousElement = NULL;
|
||||
ListFlowControll = NULL;
|
||||
|
||||
disabled = false;
|
||||
WebhookEnable = false;
|
||||
WebhookUploadImg = 0;
|
||||
}
|
||||
|
||||
ClassFlowWebhook::ClassFlowWebhook()
|
||||
@@ -35,7 +41,7 @@ ClassFlowWebhook::ClassFlowWebhook()
|
||||
SetInitialParameter();
|
||||
}
|
||||
|
||||
ClassFlowWebhook::ClassFlowWebhook(std::vector<ClassFlow*>* lfc)
|
||||
ClassFlowWebhook::ClassFlowWebhook(std::vector<ClassFlow *> *lfc)
|
||||
{
|
||||
SetInitialParameter();
|
||||
|
||||
@@ -44,17 +50,17 @@ ClassFlowWebhook::ClassFlowWebhook(std::vector<ClassFlow*>* lfc)
|
||||
{
|
||||
if (((*ListFlowControll)[i])->name().compare("ClassFlowPostProcessing") == 0)
|
||||
{
|
||||
flowpostprocessing = (ClassFlowPostProcessing*) (*ListFlowControll)[i];
|
||||
}
|
||||
if (((*ListFlowControll)[i])->name().compare("ClassFlowAlignment") == 0)
|
||||
{
|
||||
flowAlignment = (ClassFlowAlignment*) (*ListFlowControll)[i];
|
||||
flowpostprocessing = (ClassFlowPostProcessing *)(*ListFlowControll)[i];
|
||||
}
|
||||
|
||||
if (((*ListFlowControll)[i])->name().compare("ClassFlowAlignment") == 0)
|
||||
{
|
||||
flowAlignment = (ClassFlowAlignment *)(*ListFlowControll)[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ClassFlowWebhook::ClassFlowWebhook(std::vector<ClassFlow*>* lfc, ClassFlow *_prev)
|
||||
ClassFlowWebhook::ClassFlowWebhook(std::vector<ClassFlow *> *lfc, ClassFlow *_prev)
|
||||
{
|
||||
SetInitialParameter();
|
||||
|
||||
@@ -65,107 +71,131 @@ ClassFlowWebhook::ClassFlowWebhook(std::vector<ClassFlow*>* lfc, ClassFlow *_pre
|
||||
{
|
||||
if (((*ListFlowControll)[i])->name().compare("ClassFlowPostProcessing") == 0)
|
||||
{
|
||||
flowpostprocessing = (ClassFlowPostProcessing*) (*ListFlowControll)[i];
|
||||
flowpostprocessing = (ClassFlowPostProcessing *)(*ListFlowControll)[i];
|
||||
}
|
||||
|
||||
if (((*ListFlowControll)[i])->name().compare("ClassFlowAlignment") == 0)
|
||||
{
|
||||
flowAlignment = (ClassFlowAlignment*) (*ListFlowControll)[i];
|
||||
flowAlignment = (ClassFlowAlignment *)(*ListFlowControll)[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool ClassFlowWebhook::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
bool ClassFlowWebhook::ReadParameter(FILE *pFile, std::string &aktparamgraph)
|
||||
{
|
||||
std::vector<string> splitted;
|
||||
|
||||
aktparamgraph = trim(aktparamgraph);
|
||||
printf("akt param: %s\n", aktparamgraph.c_str());
|
||||
|
||||
aktparamgraph = trim_string_left_right(aktparamgraph);
|
||||
if (aktparamgraph.size() == 0)
|
||||
if (!this->GetNextParagraph(pfile, aktparamgraph))
|
||||
return false;
|
||||
|
||||
if (toUpper(aktparamgraph).compare("[WEBHOOK]") != 0)
|
||||
return false;
|
||||
|
||||
|
||||
|
||||
while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph))
|
||||
{
|
||||
ESP_LOGD(TAG, "while loop reading line: %s", aktparamgraph.c_str());
|
||||
splitted = ZerlegeZeile(aktparamgraph);
|
||||
std::string _param = GetParameterName(splitted[0]);
|
||||
if (!GetNextParagraph(pFile, aktparamgraph))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ((toUpper(_param) == "URI") && (splitted.size() > 1))
|
||||
if ((to_upper(aktparamgraph).compare("[WEBHOOK]") != 0) && (to_upper(aktparamgraph).compare(";[WEBHOOK]") != 0))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (aktparamgraph[0] == ';')
|
||||
{
|
||||
Webhook_controll_config.enabled = false;
|
||||
while (getNextLine(pFile, &aktparamgraph) && !isNewParagraph(aktparamgraph));
|
||||
ESP_LOGD(TAG, "Webhook is disabled!");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<std::string> splitted;
|
||||
|
||||
while (getNextLine(pFile, &aktparamgraph) && !isNewParagraph(aktparamgraph))
|
||||
{
|
||||
splitted = split_line(aktparamgraph);
|
||||
|
||||
if (splitted.size() > 1)
|
||||
{
|
||||
this->uri = splitted[1];
|
||||
}
|
||||
if (((toUpper(_param) == "APIKEY")) && (splitted.size() > 1))
|
||||
{
|
||||
this->apikey = splitted[1];
|
||||
}
|
||||
if (((toUpper(_param) == "UPLOADIMG")) && (splitted.size() > 1))
|
||||
{
|
||||
if (toUpper(splitted[1]) == "1")
|
||||
std::string _param = to_upper(GetParameterName(splitted[0]));
|
||||
|
||||
if (_param == "URI")
|
||||
{
|
||||
this->WebhookUploadImg = 1;
|
||||
} else if (toUpper(splitted[1]) == "2")
|
||||
Webhook_controll_config.uri = splitted[1];
|
||||
}
|
||||
else if (_param == "APIKEY")
|
||||
{
|
||||
this->WebhookUploadImg = 2;
|
||||
Webhook_controll_config.apikey = splitted[1];
|
||||
}
|
||||
else if (_param == "UPLOADIMG")
|
||||
{
|
||||
if (to_upper(splitted[1]) == "1")
|
||||
{
|
||||
Webhook_controll_config.uploadImg = 1;
|
||||
}
|
||||
else if (to_upper(splitted[1]) == "2")
|
||||
{
|
||||
Webhook_controll_config.uploadImg = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WebhookInit(uri,apikey);
|
||||
WebhookEnable = true;
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Webhook Enabled for Uri " + uri);
|
||||
if ((Webhook_controll_config.uri.length() > 0) && (Webhook_controll_config.apikey.length() > 0))
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Init Webhook with uri: " + Webhook_controll_config.uri + ", apikey: *****");
|
||||
WebhookInit(Webhook_controll_config.uri, Webhook_controll_config.apikey);
|
||||
Webhook_controll_config.enabled = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Webhook init skipped as we are missing some parameters");
|
||||
Webhook_controll_config.enabled = false;
|
||||
}
|
||||
|
||||
printf("uri: %s\n", uri.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void ClassFlowWebhook::handleMeasurement(string _decsep, string _value)
|
||||
void ClassFlowWebhook::handleMeasurement(std::string _decsep, std::string _value)
|
||||
{
|
||||
string _digit, _decpos;
|
||||
std::string _digit;
|
||||
int _pospunkt = _decsep.find_first_of(".");
|
||||
// ESP_LOGD(TAG, "Name: %s, Pospunkt: %d", _decsep.c_str(), _pospunkt);
|
||||
|
||||
if (_pospunkt > -1)
|
||||
{
|
||||
_digit = _decsep.substr(0, _pospunkt);
|
||||
}
|
||||
else
|
||||
{
|
||||
_digit = "default";
|
||||
}
|
||||
|
||||
for (int j = 0; j < flowpostprocessing->NUMBERS.size(); ++j)
|
||||
{
|
||||
if (_digit == "default") // Set to default first (if nothing else is set)
|
||||
{
|
||||
flowpostprocessing->NUMBERS[j]->MeasurementV2 = _value;
|
||||
}
|
||||
if (flowpostprocessing->NUMBERS[j]->name == _digit)
|
||||
// Set to default first (if nothing else is set)
|
||||
if ((_digit == "default") || (flowpostprocessing->NUMBERS[j]->name == _digit))
|
||||
{
|
||||
flowpostprocessing->NUMBERS[j]->MeasurementV2 = _value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool ClassFlowWebhook::doFlow(string zwtime)
|
||||
bool ClassFlowWebhook::doFlow(std::string temp_time)
|
||||
{
|
||||
if (!WebhookEnable)
|
||||
if (!Webhook_controll_config.enabled)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (flowpostprocessing)
|
||||
{
|
||||
printf("vor sende WebHook");
|
||||
bool numbersWithError = WebhookPublish(flowpostprocessing->GetNumbers());
|
||||
|
||||
#ifdef ALGROI_LOAD_FROM_MEM_AS_JPG
|
||||
if ((WebhookUploadImg == 1 || (WebhookUploadImg != 0 && numbersWithError)) && flowAlignment && flowAlignment->AlgROI) {
|
||||
WebhookUploadPic(flowAlignment->AlgROI);
|
||||
}
|
||||
#endif
|
||||
#ifdef ALGROI_LOAD_FROM_MEM_AS_JPG
|
||||
if ((Webhook_controll_config.uploadImg == 1 || (Webhook_controll_config.uploadImg != 0 && numbersWithError)) && flowAlignment && flowAlignment->AlgROI)
|
||||
{
|
||||
WebhookUploadPic(flowAlignment->AlgROI);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif //ENABLE_WEBHOOK
|
||||
@@ -1,43 +1,42 @@
|
||||
#ifdef ENABLE_WEBHOOK
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef CLASSFWEBHOOK_H
|
||||
#define CLASSFWEBHOOK_H
|
||||
|
||||
#include "ClassFlow.h"
|
||||
#include <string>
|
||||
|
||||
#include "ClassFlow.h"
|
||||
#include "ClassFlowPostProcessing.h"
|
||||
#include "ClassFlowAlignment.h"
|
||||
|
||||
#include <string>
|
||||
typedef struct
|
||||
{
|
||||
bool enabled;
|
||||
std::string uri;
|
||||
std::string apikey;
|
||||
int uploadImg;
|
||||
std::string oldValue;
|
||||
} Webhook_controll_config_t;
|
||||
|
||||
class ClassFlowWebhook :
|
||||
public ClassFlow
|
||||
extern Webhook_controll_config_t Webhook_controll_config;
|
||||
|
||||
class ClassFlowWebhook : public ClassFlow
|
||||
{
|
||||
protected:
|
||||
std::string uri, apikey;
|
||||
ClassFlowPostProcessing* flowpostprocessing;
|
||||
ClassFlowAlignment* flowAlignment;
|
||||
|
||||
bool WebhookEnable;
|
||||
int WebhookUploadImg;
|
||||
ClassFlowPostProcessing *flowpostprocessing;
|
||||
ClassFlowAlignment *flowAlignment;
|
||||
|
||||
void SetInitialParameter(void);
|
||||
|
||||
void handleFieldname(string _decsep, string _value);
|
||||
void handleMeasurement(string _decsep, string _value);
|
||||
|
||||
void handleMeasurement(std::string _decsep, std::string _value);
|
||||
|
||||
public:
|
||||
ClassFlowWebhook();
|
||||
ClassFlowWebhook(std::vector<ClassFlow*>* lfc);
|
||||
ClassFlowWebhook(std::vector<ClassFlow*>* lfc, ClassFlow *_prev);
|
||||
ClassFlowWebhook(std::vector<ClassFlow *> *lfc);
|
||||
ClassFlowWebhook(std::vector<ClassFlow *> *lfc, ClassFlow *_prev);
|
||||
|
||||
bool ReadParameter(FILE* pfile, string& aktparamgraph);
|
||||
bool doFlow(string time);
|
||||
string name(){return "ClassFlowWebhook";};
|
||||
bool ReadParameter(FILE *pFile, std::string &aktparamgraph);
|
||||
bool doFlow(std::string temp_time);
|
||||
std::string name() { return "ClassFlowWebhook"; };
|
||||
};
|
||||
|
||||
#endif //CLASSFWEBHOOK_H
|
||||
#endif //ENABLE_WEBHOOK
|
||||
#endif // CLASSFWEBHOOK_H
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,75 +3,16 @@
|
||||
#ifndef MAINFLOWCONTROL_H
|
||||
#define MAINFLOWCONTROL_H
|
||||
|
||||
#include <esp_log.h>
|
||||
#include <string>
|
||||
|
||||
#include <esp_log.h>
|
||||
#include <esp_http_server.h>
|
||||
|
||||
#include "CImageBasis.h"
|
||||
#include "ClassFlowControll.h"
|
||||
#include "openmetrics.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint16_t CamSensor_id;
|
||||
|
||||
framesize_t ImageFrameSize = FRAMESIZE_VGA; // 0 - 10
|
||||
gainceiling_t ImageGainceiling; // Image gain (GAINCEILING_x2, x4, x8, x16, x32, x64 or x128)
|
||||
|
||||
int ImageQuality; // 0 - 63
|
||||
int ImageBrightness; // (-2 to 2) - set brightness
|
||||
int ImageContrast; //-2 - 2
|
||||
int ImageSaturation; //-2 - 2
|
||||
int ImageSharpness; //-2 - 2
|
||||
bool ImageAutoSharpness;
|
||||
int ImageSpecialEffect; // 0 - 6
|
||||
int ImageWbMode; // 0 to 4 - if awb_gain enabled (0 - Auto, 1 - Sunny, 2 - Cloudy, 3 - Office, 4 - Home)
|
||||
int ImageAwb; // white balance enable (0 or 1)
|
||||
int ImageAwbGain; // Auto White Balance enable (0 or 1)
|
||||
int ImageAec; // auto exposure off (1 or 0)
|
||||
int ImageAec2; // automatic exposure sensor (0 or 1)
|
||||
int ImageAeLevel; // auto exposure levels (-2 to 2)
|
||||
int ImageAecValue; // set exposure manually (0-1200)
|
||||
int ImageAgc; // auto gain off (1 or 0)
|
||||
int ImageAgcGain; // set gain manually (0 - 30)
|
||||
int ImageBpc; // black pixel correction
|
||||
int ImageWpc; // white pixel correction
|
||||
int ImageRawGma; // (1 or 0)
|
||||
int ImageLenc; // lens correction (1 or 0)
|
||||
int ImageHmirror; // (0 or 1) flip horizontally
|
||||
int ImageVflip; // Invert image (0 or 1)
|
||||
int ImageDcw; // downsize enable (1 or 0)
|
||||
|
||||
int ImageDenoiseLevel; // The OV2640 does not support it, OV3660 and OV5640 (0 to 8)
|
||||
|
||||
int ImageWidth;
|
||||
int ImageHeight;
|
||||
|
||||
int ImageLedIntensity;
|
||||
|
||||
bool ImageZoomEnabled;
|
||||
int ImageZoomOffsetX;
|
||||
int ImageZoomOffsetY;
|
||||
int ImageZoomSize;
|
||||
|
||||
int WaitBeforePicture;
|
||||
bool isImageSize;
|
||||
|
||||
bool CameraInitSuccessful;
|
||||
bool changedCameraSettings;
|
||||
bool DemoMode;
|
||||
bool SaveAllFiles;
|
||||
} camera_flow_config_temp_t;
|
||||
|
||||
extern camera_flow_config_temp_t CFstatus;
|
||||
extern ClassFlowControll flowctrl;
|
||||
|
||||
esp_err_t setCCstatusToCFstatus(void); // CCstatus >>> CFstatus
|
||||
esp_err_t setCFstatusToCCstatus(void); // CFstatus >>> CCstatus
|
||||
esp_err_t setCFstatusToCam(void); // CFstatus >>> Kamera
|
||||
|
||||
void register_server_main_flow_task_uri(httpd_handle_t server);
|
||||
|
||||
void CheckIsPlannedReboot(void);
|
||||
bool getIsPlannedReboot(void);
|
||||
|
||||
@@ -81,11 +22,11 @@ bool isSetupModusActive(void);
|
||||
|
||||
int getCountFlowRounds(void);
|
||||
|
||||
#ifdef ENABLE_MQTT
|
||||
esp_err_t MQTTCtrlFlowStart(std::string _topic);
|
||||
#endif // ENABLE_MQTT
|
||||
|
||||
esp_err_t GetRawJPG(httpd_req_t *req);
|
||||
esp_err_t GetJPG(std::string _filename, httpd_req_t *req);
|
||||
|
||||
void main_flow_register_uri(httpd_handle_t server);
|
||||
|
||||
#endif // MAINFLOWCONTROL_H
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
|
||||
|
||||
idf_component_register(SRCS ${app_sources}
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES esp_timer esp-tflite-micro jomjol_logfile fatfs sdmmc vfs)
|
||||
INCLUDE_DIRS "." "../../include"
|
||||
REQUIRES driver esp_driver_uart log esp_timer esp-tflite-micro jomjol_logfile fatfs sdmmc vfs)
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,113 +3,148 @@
|
||||
#ifndef HELPER_H
|
||||
#define HELPER_H
|
||||
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
|
||||
#include "sdmmc_cmd.h"
|
||||
|
||||
#include "defines.h"
|
||||
|
||||
#ifdef CONFIG_SOC_TEMP_SENSOR_SUPPORTED
|
||||
#include <driver/temperature_sensor.h>
|
||||
#endif
|
||||
|
||||
#if (ESP_IDF_VERSION_MAJOR >= 5)
|
||||
#include "soc/periph_defs.h"
|
||||
#include "esp_private/periph_ctrl.h"
|
||||
#include "soc/gpio_sig_map.h"
|
||||
#include "soc/gpio_periph.h"
|
||||
#include "soc/io_mux_reg.h"
|
||||
#include "esp_rom_gpio.h"
|
||||
#define gpio_pad_select_gpio esp_rom_gpio_pad_select_gpio
|
||||
#define gpio_matrix_in(a, b, c) esp_rom_gpio_connect_in_signal(a, b, c)
|
||||
#define gpio_matrix_out(a, b, c, d) esp_rom_gpio_connect_out_signal(a, b, c, d)
|
||||
#define ets_delay_us(a) esp_rom_delay_us(a)
|
||||
#endif
|
||||
|
||||
using namespace std;
|
||||
|
||||
std::string FormatFileName(std::string input);
|
||||
std::size_t file_size(const std::string& file_name);
|
||||
void FindReplace(std::string& line, std::string& oldString, std::string& newString);
|
||||
|
||||
bool CopyFile(string input, string output);
|
||||
bool DeleteFile(string filename);
|
||||
bool RenameFile(string from, string to);
|
||||
bool RenameFolder(string from, string to);
|
||||
bool MakeDir(std::string _what);
|
||||
bool FileExists(string filename);
|
||||
bool FolderExists(string foldername);
|
||||
|
||||
string RundeOutput(double _in, int _anzNachkomma);
|
||||
|
||||
size_t findDelimiterPos(string input, string delimiter);
|
||||
//string trim(string istring);
|
||||
string trim(string istring, string adddelimiter = "");
|
||||
bool ctype_space(const char c, string adddelimiter);
|
||||
|
||||
string getFileType(string filename);
|
||||
string getFileFullFileName(string filename);
|
||||
string getDirectory(string filename);
|
||||
|
||||
int mkdir_r(const char *dir, const mode_t mode);
|
||||
int removeFolder(const char* folderPath, const char* logTag);
|
||||
|
||||
string toLower(string in);
|
||||
string toUpper(string in);
|
||||
|
||||
float temperatureRead();
|
||||
|
||||
std::string intToHexString(int _valueInt);
|
||||
time_t addDays(time_t startTime, int days);
|
||||
|
||||
void memCopyGen(uint8_t* _source, uint8_t* _target, int _size);
|
||||
|
||||
std::vector<string> HelperZerlegeZeile(std::string input, std::string _delimiter);
|
||||
std::vector<std::string> ZerlegeZeile(std::string input, std::string delimiter = " =, \t");
|
||||
|
||||
///////////////////////////
|
||||
size_t getInternalESPHeapSize();
|
||||
size_t getESPHeapSize();
|
||||
string getESPHeapInfo();
|
||||
|
||||
/////////////////////////////
|
||||
string getSDCardPartitionSize();
|
||||
string getSDCardFreePartitionSpace();
|
||||
string getSDCardPartitionAllocationSize();
|
||||
|
||||
void SaveSDCardInfo(sdmmc_card_t* card);
|
||||
string SDCardParseManufacturerIDs(int);
|
||||
string getSDCardManufacturer();
|
||||
string getSDCardName();
|
||||
string getSDCardCapacity();
|
||||
string getSDCardSectorSize();
|
||||
|
||||
string getMac(void);
|
||||
|
||||
/* Error bit fields
|
||||
One bit per error
|
||||
Make sure it matches https://jomjol.github.io/AI-on-the-edge-device-docs/Error-Codes */
|
||||
enum SystemStatusFlag_t { // One bit per error
|
||||
// First Byte
|
||||
SYSTEM_STATUS_PSRAM_BAD = 1 << 0, // 1, Critical Error
|
||||
SYSTEM_STATUS_HEAP_TOO_SMALL = 1 << 1, // 2, Critical Error
|
||||
SYSTEM_STATUS_CAM_BAD = 1 << 2, // 4, Critical Error
|
||||
SYSTEM_STATUS_SDCARD_CHECK_BAD = 1 << 3, // 8, Critical Error
|
||||
SYSTEM_STATUS_FOLDER_CHECK_BAD = 1 << 4, // 16, Critical Error
|
||||
enum SystemStatusFlag_t
|
||||
{ // One bit per error
|
||||
// First Byte
|
||||
SYSTEM_STATUS_PSRAM_BAD = 1 << 0, // 1, Critical Error
|
||||
SYSTEM_STATUS_HEAP_TOO_SMALL = 1 << 1, // 2, Critical Error
|
||||
SYSTEM_STATUS_CAM_BAD = 1 << 2, // 4, Critical Error
|
||||
SYSTEM_STATUS_SDCARD_CHECK_BAD = 1 << 3, // 8, Critical Error
|
||||
SYSTEM_STATUS_FOLDER_CHECK_BAD = 1 << 4, // 16, Critical Error
|
||||
|
||||
// Second Byte
|
||||
SYSTEM_STATUS_CAM_FB_BAD = 1 << (0+8), // 8, Flow still might work
|
||||
SYSTEM_STATUS_NTP_BAD = 1 << (1+8), // 9, Flow will work but time will be wrong
|
||||
// Second Byte
|
||||
SYSTEM_STATUS_CAM_FB_BAD = 1 << (0 + 8), // 8, Flow still might work
|
||||
SYSTEM_STATUS_NTP_BAD = 1 << (1 + 8), // 9, Flow will work but time will be wrong
|
||||
};
|
||||
|
||||
void setSystemStatusFlag(SystemStatusFlag_t flag);
|
||||
void clearSystemStatusFlag(SystemStatusFlag_t flag);
|
||||
int getSystemStatus(void);
|
||||
bool isSetSystemStatusFlag(SystemStatusFlag_t flag);
|
||||
void string_to_ip4(const char *ip, int &a, int &b, int &c, int &d);
|
||||
std::string bssid_to_string(const char *c);
|
||||
|
||||
time_t getUpTime(void);
|
||||
string getResetReason(void);
|
||||
std::string getFormatedUptime(bool compact);
|
||||
std::string format_filename(std::string input);
|
||||
std::size_t file_size(const std::string &file_name);
|
||||
void find_replace(std::string &line, std::string &oldString, std::string &newString);
|
||||
|
||||
const char* get404(void);
|
||||
bool copy_file(string input, string output);
|
||||
bool delete_file(string filename);
|
||||
bool rename_file(string from, string to);
|
||||
bool rename_folder(string from, string to);
|
||||
bool make_dir(std::string _what);
|
||||
bool file_exists(string filename);
|
||||
bool folder_exists(string foldername);
|
||||
|
||||
std::string UrlDecode(const std::string& value);
|
||||
string round_output(double _in, int _anzNachkomma);
|
||||
|
||||
void replaceAll(std::string& s, const std::string& toReplace, const std::string& replaceWith);
|
||||
bool replaceString(std::string& s, std::string const& toReplace, std::string const& replaceWith);
|
||||
bool replaceString(std::string& s, std::string const& toReplace, std::string const& replaceWith, bool logIt);
|
||||
bool isInString(std::string& s, std::string const& toFind);
|
||||
size_t find_delimiter_pos(string input, string delimiter);
|
||||
|
||||
bool isStringNumeric(std::string &input);
|
||||
bool isStringAlphabetic(std::string &input);
|
||||
bool isStringAlphanumeric(std::string &input);
|
||||
bool alphanumericToBoolean(std::string &input);
|
||||
std::string trim_string_left_right(std::string istring, std::string adddelimiter = "");
|
||||
std::string trim_string_left(std::string istring, std::string adddelimiter = "");
|
||||
std::string trim_string_right(std::string istring, std::string adddelimiter = "");
|
||||
|
||||
int clipInt(int input, int high, int low);
|
||||
bool numericStrToBool(std::string input);
|
||||
bool stringToBoolean(std::string input);
|
||||
bool ctype_space(const char c, string adddelimiter);
|
||||
|
||||
#endif //HELPER_H
|
||||
string get_file_type(string filename);
|
||||
string get_file_full_filename(string filename);
|
||||
string get_directory(string filename);
|
||||
|
||||
int mkdir_r(const char *dir, const mode_t mode);
|
||||
int remove_folder(const char *folderPath, const char *logTag);
|
||||
|
||||
string to_lower(string in);
|
||||
string to_upper(string in);
|
||||
|
||||
static float temp_sens_value = -1;
|
||||
#ifdef CONFIG_SOC_TEMP_SENSOR_SUPPORTED
|
||||
void init_tempsensor(void);
|
||||
#endif
|
||||
float read_tempsensor(void);
|
||||
|
||||
time_t add_days(time_t startTime, int days);
|
||||
void mem_copy_gen(uint8_t *_source, uint8_t *_target, int _size);
|
||||
|
||||
std::vector<std::string> split_string(const std::string &str);
|
||||
std::vector<std::string> split_line(std::string input, std::string _delimiter = "=");
|
||||
|
||||
///////////////////////////
|
||||
size_t get_internal_heapsize();
|
||||
size_t get_heapsize();
|
||||
string get_heapinfo();
|
||||
|
||||
/////////////////////////////
|
||||
string get_sd_card_partition_size();
|
||||
string get_sd_card_free_partition_space();
|
||||
string get_sd_card_partition_allocation_size();
|
||||
|
||||
void save_sd_card_info(sdmmc_card_t *card);
|
||||
string sd_card_parse_manufacturer_ids(int);
|
||||
string get_sd_card_manufacturer();
|
||||
string get_sd_card_name();
|
||||
string get_sd_card_capacity();
|
||||
string get_sd_card_sector_size();
|
||||
|
||||
string get_mac(void);
|
||||
|
||||
void set_system_statusflag(SystemStatusFlag_t flag);
|
||||
void clear_system_statusflag(SystemStatusFlag_t flag);
|
||||
int get_system_status(void);
|
||||
bool is_set_system_statusflag(SystemStatusFlag_t flag);
|
||||
|
||||
time_t get_uptime(void);
|
||||
string get_reset_reason(void);
|
||||
std::string get_formated_uptime(bool compact);
|
||||
|
||||
const char *get404(void);
|
||||
|
||||
std::string url_decode(const std::string &value);
|
||||
|
||||
void replace_all(std::string &s, const std::string &toReplace, const std::string &replaceWith);
|
||||
bool replace_string(std::string &s, std::string const &toReplace, std::string const &replaceWith);
|
||||
bool replace_string(std::string &s, std::string const &toReplace, std::string const &replaceWith, bool logIt);
|
||||
|
||||
std::string encrypt_decrypt_string(std::string toEncrypt);
|
||||
std::string encrypt_pw_string(std::string toEncrypt);
|
||||
std::string decrypt_pw_string(std::string toDecrypt);
|
||||
esp_err_t encrypt_decrypt_pw_on_sd(bool _encrypt, std::string filename);
|
||||
|
||||
bool is_in_string(std::string &s, std::string const &toFind);
|
||||
bool is_string_numeric(std::string &input);
|
||||
bool is_string_alphabetic(std::string &input);
|
||||
bool is_string_alphanumeric(std::string &input);
|
||||
bool alphanumeric_to_boolean(std::string &input);
|
||||
|
||||
int clip_int(int input, int high, int low);
|
||||
bool numeric_str_to_boolean(std::string input);
|
||||
bool string_to_boolean(std::string input);
|
||||
|
||||
#endif // HELPER_H
|
||||
|
||||
@@ -1,179 +0,0 @@
|
||||
#include "../../include/defines.h"
|
||||
|
||||
#ifdef DEBUG_ENABLE_SYSINFO
|
||||
|
||||
#include "esp_sys.h"
|
||||
#include "esp_chip_info.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
#include "esp_chip_info.h"
|
||||
|
||||
|
||||
void Restart() {
|
||||
esp_restart();
|
||||
}
|
||||
|
||||
//source : https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/misc_system_api.html#_CPPv416esp_chip_model_t
|
||||
|
||||
//https://github.com/espressif/esp-idf/blob/8464186e67e34b417621df6b6f1f289a6c60b859/components/esp_hw_support/include/esp_chip_info.h
|
||||
/*
|
||||
typedef enum {
|
||||
CHIP_ESP32 = 1, //!< ESP32
|
||||
CHIP_ESP32S2 = 2, //!< ESP32-S2
|
||||
CHIP_ESP32S3 = 9, //!< ESP32-S3
|
||||
CHIP_ESP32C3 = 5, //!< ESP32-C3
|
||||
CHIP_ESP32H4 = 6, //!< ESP32-H4
|
||||
CHIP_ESP32C2 = 12, //!< ESP32-C2
|
||||
CHIP_ESP32C6 = 13, //!< ESP32-C6
|
||||
CHIP_ESP32H2 = 16, //!< ESP32-H2
|
||||
CHIP_POSIX_LINUX = 999, //!< The code is running on POSIX/Linux simulator
|
||||
} esp_chip_model_t;
|
||||
*/
|
||||
|
||||
char* GetChipModel(){
|
||||
esp_chip_info_t chipInfo;
|
||||
esp_chip_info(&chipInfo);
|
||||
switch((int)chipInfo.model) {
|
||||
case 0 : return (char*)"ESP8266";
|
||||
case (int)esp_chip_model_t::CHIP_ESP32 : return (char*)"ESP32";
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 0, 0)
|
||||
case (int)esp_chip_model_t::CHIP_ESP32S2 : return (char*)"ESP32-S2";
|
||||
case (int)esp_chip_model_t::CHIP_ESP32S3 : return (char*)"ESP32-S3";
|
||||
case (int)esp_chip_model_t::CHIP_ESP32C3 : return (char*)"ESP32-C3";
|
||||
case 6 : return (char*)"ESP32-H4";
|
||||
case 12 : return (char*)"ESP32-C2";
|
||||
case 13 : return (char*)"ESP32-C6";
|
||||
//case (int)esp_chip_model_t::CHIP_ESP32H4 : return (char*)"ESP32-H4";
|
||||
//case (int)esp_chip_model_t::CHIP_ESP32C2 : return (char*)"ESP32-C2";
|
||||
//case (int)esp_chip_model_t::CHIP_ESP32C6 : return (char*)"ESP32-C6";
|
||||
//case (int)esp_chip_model_t::CHIP_ESP32H2 : return (char*)"ESP32-H2";
|
||||
case 16 : return (char*)"ESP32-H2";
|
||||
//case (int)esp_chip_model_t::CHIP_POSIX_LINUX : return (char*)"CHIP_POSIX_LINUX";
|
||||
|
||||
#endif
|
||||
}
|
||||
return (char*)"Chip Unknown";
|
||||
}
|
||||
|
||||
uint8_t GetChipCoreCount() {
|
||||
esp_chip_info_t chipInfo;
|
||||
esp_chip_info(&chipInfo);
|
||||
return chipInfo.cores;
|
||||
}
|
||||
|
||||
uint16_t GetChipRevision() {
|
||||
esp_chip_info_t chipInfo;
|
||||
esp_chip_info(&chipInfo);
|
||||
return chipInfo.revision;
|
||||
}
|
||||
|
||||
uint32_t GetChipfeatures() {
|
||||
esp_chip_info_t chipInfo;
|
||||
esp_chip_info(&chipInfo);
|
||||
return chipInfo.features;
|
||||
}
|
||||
|
||||
|
||||
uint32_t GetFreeHeap() {
|
||||
return esp_get_free_heap_size();
|
||||
}
|
||||
|
||||
uint32_t GetLeastHeapFreeSinceBoot() {
|
||||
return esp_get_minimum_free_heap_size();
|
||||
}
|
||||
|
||||
|
||||
std::string get_device_info()
|
||||
{
|
||||
esp_chip_info_t chip_info;
|
||||
esp_chip_info(&chip_info);
|
||||
|
||||
std::string espInfoResultStr = "";
|
||||
char aMsgBuf[40];
|
||||
|
||||
espInfoResultStr += "Device Info:";
|
||||
espInfoResultStr += "---------------\n";
|
||||
espInfoResultStr += "Chip Model: " + std::string(GetChipModel()) +"\n";
|
||||
sprintf(aMsgBuf,"Chip Revision: %d\n", chip_info.revision);
|
||||
espInfoResultStr += std::string(aMsgBuf);
|
||||
sprintf(aMsgBuf,"CPU Cores: %d\n", chip_info.cores);
|
||||
espInfoResultStr += std::string(aMsgBuf);
|
||||
sprintf(aMsgBuf,"Flash Memory: %dMB\n", spi_flash_get_chip_size()/(1024*1024));
|
||||
espInfoResultStr += std::string(aMsgBuf);
|
||||
if(chip_info.features & CHIP_FEATURE_WIFI_BGN)
|
||||
//espInfoResultStr += "Base MAC: " + std::string(getMac()) +"\n";
|
||||
espInfoResultStr += "ESP-IDF version: " + std::string(esp_get_idf_version()) +"\n";
|
||||
if((chip_info.features & CHIP_FEATURE_WIFI_BGN) || (chip_info.features & CHIP_FEATURE_BT) ||
|
||||
(chip_info.features & CHIP_FEATURE_BLE) || (chip_info.features & CHIP_FEATURE_EMB_FLASH))
|
||||
{
|
||||
espInfoResultStr += "Characteristics:\n";
|
||||
if(chip_info.features & CHIP_FEATURE_WIFI_BGN)
|
||||
espInfoResultStr += " WiFi 2.4GHz\n";
|
||||
if(chip_info.features & CHIP_FEATURE_BT)
|
||||
espInfoResultStr += " Bluetooth Classic\n";
|
||||
if(chip_info.features & CHIP_FEATURE_BLE)
|
||||
espInfoResultStr += " Bluetooth Low Energy\n";
|
||||
if(chip_info.features & CHIP_FEATURE_EMB_FLASH)
|
||||
espInfoResultStr += " Embedded Flash memory\n";
|
||||
else
|
||||
espInfoResultStr += " External Flash memory\n";
|
||||
}
|
||||
|
||||
#ifdef USE_HIMEM_IF_AVAILABLE
|
||||
sprintf(aMsgBuf,"spiram size %u\n", esp_psram_get_size());
|
||||
espInfoResultStr += std::string(aMsgBuf);
|
||||
sprintf(aMsgBuf,"himem free %u\n", esp_himem_get_free_size());
|
||||
espInfoResultStr += std::string(aMsgBuf);
|
||||
sprintf(aMsgBuf,"himem phys %u\n", esp_himem_get_phys_size());
|
||||
espInfoResultStr += std::string(aMsgBuf);
|
||||
sprintf(aMsgBuf,"himem reserved %u\n", esp_himem_reserved_area_size());
|
||||
espInfoResultStr += std::string(aMsgBuf);
|
||||
#endif
|
||||
|
||||
return espInfoResultStr;
|
||||
}
|
||||
|
||||
|
||||
size_t getFreeMemoryInternal(){ //Current Free Memory
|
||||
return heap_caps_get_free_size(MALLOC_CAP_8BIT) - heap_caps_get_free_size(MALLOC_CAP_SPIRAM);
|
||||
}
|
||||
|
||||
size_t getFreeMemorySPIRAM(){ //Current Free Memory
|
||||
return heap_caps_get_free_size(MALLOC_CAP_SPIRAM);
|
||||
}
|
||||
|
||||
|
||||
size_t getLargestFreeBlockInternal(){ //Largest Free Block
|
||||
return heap_caps_get_largest_free_block(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
|
||||
}
|
||||
|
||||
size_t getLargestFreeBlockSPIRAM(){ //Largest Free Block
|
||||
return heap_caps_get_largest_free_block(MALLOC_CAP_SPIRAM);
|
||||
}
|
||||
|
||||
|
||||
size_t getMinEverFreeMemInternal(){ //Min. Ever Free Size
|
||||
return heap_caps_get_minimum_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
|
||||
}
|
||||
|
||||
size_t getMinEverFreeMemSPIRAM(){ //Min. Ever Free Size
|
||||
return heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM);
|
||||
}
|
||||
|
||||
#ifdef USE_HIMEM_IF_AVAILABLE
|
||||
size_t getHimemTotSpace(){
|
||||
return esp_himem_get_phys_size();
|
||||
}
|
||||
|
||||
size_t getHimemFreeSpace(){
|
||||
return esp_himem_get_free_size();
|
||||
}
|
||||
|
||||
size_t getHimemReservedArea(){
|
||||
return esp_himem_reserved_area_size();
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //DEBUG_ENABLE_SYSINFO
|
||||
@@ -1,54 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../include/defines.h"
|
||||
|
||||
#ifdef DEBUG_ENABLE_SYSINFO
|
||||
|
||||
#ifndef ESP_SYS_H
|
||||
#define ESP_SYS_H
|
||||
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
// Device libraries (ESP-IDF)
|
||||
#include <esp_system.h>
|
||||
#include <esp_spi_flash.h>
|
||||
#include <esp_heap_caps.h>
|
||||
|
||||
// for esp_psram_get_size
|
||||
extern "C" {
|
||||
#include "esp_psram.h"
|
||||
#ifdef USE_HIMEM_IF_AVAILABLE
|
||||
#include <esp32/himem.h>
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Restart();
|
||||
char *GetChipModel();
|
||||
uint8_t GetChipCoreCount();
|
||||
uint16_t GetChipRevision();
|
||||
uint32_t GetChipfeatures();
|
||||
uint32_t GetFreeHeap();
|
||||
uint32_t GetLeastHeapFreeSinceBoot();
|
||||
|
||||
std::string get_device_info();
|
||||
|
||||
size_t getFreeMemoryInternal();
|
||||
size_t getFreeMemorySPIRAM();
|
||||
size_t getLargestFreeBlockInternal();
|
||||
size_t getLargestFreeBlockSPIRAM();
|
||||
size_t getMinEverFreeMemInternal();
|
||||
size_t getMinEverFreeMemSPIRAM();
|
||||
#ifdef USE_HIMEM_IF_AVAILABLE
|
||||
size_t getHimemTotSpace();
|
||||
size_t getHimemFreeSpace();
|
||||
size_t getHimemReservedArea();
|
||||
#endif
|
||||
|
||||
|
||||
#endif //ESP_SYS_H
|
||||
|
||||
#endif // DEBUG_ENABLE_SYSINFO
|
||||
@@ -1,115 +0,0 @@
|
||||
|
||||
// need [env:esp32cam-dev-himem]
|
||||
//CONFIG_SPIRAM_BANKSWITCH_ENABLE=y
|
||||
//CONFIG_SPIRAM_BANKSWITCH_RESERVE=4
|
||||
|
||||
|
||||
|
||||
#include "../../include/defines.h"
|
||||
|
||||
#ifdef DEBUG_HIMEM_MEMORY_CHECK
|
||||
|
||||
#include "himem_memory_check.h"
|
||||
|
||||
//source adapted from : https://github.com/espressif/esp-idf/blob/master/examples/system/himem/main/himem_example_main.c
|
||||
|
||||
|
||||
//Fill memory with pseudo-random data generated from the given seed.
|
||||
//Fills the memory in 32-bit words for speed.
|
||||
static void fill_mem_seed(int seed, void *mem, int len)
|
||||
{
|
||||
uint32_t *p = (uint32_t *)mem;
|
||||
unsigned int rseed = seed ^ 0xa5a5a5a5;
|
||||
for (int i = 0; i < len / 4; i++) {
|
||||
*p++ = rand_r(&rseed);
|
||||
}
|
||||
}
|
||||
|
||||
//Check the memory filled by fill_mem_seed. Returns true if the data matches the data
|
||||
//that fill_mem_seed wrote (when given the same seed).
|
||||
//Returns true if there's a match, false when the region differs from what should be there.
|
||||
static bool check_mem_seed(int seed, void *mem, int len, int phys_addr)
|
||||
{
|
||||
uint32_t *p = (uint32_t *)mem;
|
||||
unsigned int rseed = seed ^ 0xa5a5a5a5;
|
||||
for (int i = 0; i < len / 4; i++) {
|
||||
uint32_t ex = rand_r(&rseed);
|
||||
if (ex != *p) {
|
||||
//printf("check_mem_seed: %x has 0x%08"PRIx32" expected 0x%08"PRIx32"\n", phys_addr+((char*)p-(char*)mem), *p, ex);
|
||||
return false;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//Allocate a himem region, fill it with data, check it and release it.
|
||||
static bool test_region(int check_size, int seed)
|
||||
{
|
||||
esp_himem_handle_t mh; //Handle for the address space we're using
|
||||
esp_himem_rangehandle_t rh; //Handle for the actual RAM.
|
||||
bool ret = true;
|
||||
|
||||
//Allocate the memory we're going to check.
|
||||
ESP_ERROR_CHECK(esp_himem_alloc(check_size, &mh));
|
||||
//Allocate a block of address range
|
||||
ESP_ERROR_CHECK(esp_himem_alloc_map_range(ESP_HIMEM_BLKSZ, &rh));
|
||||
for (int i = 0; i < check_size; i += ESP_HIMEM_BLKSZ) {
|
||||
uint32_t *ptr = NULL;
|
||||
//Map in block, write pseudo-random data, unmap block.
|
||||
ESP_ERROR_CHECK(esp_himem_map(mh, rh, i, 0, ESP_HIMEM_BLKSZ, 0, (void**)&ptr));
|
||||
fill_mem_seed(i ^ seed, ptr, ESP_HIMEM_BLKSZ); //
|
||||
ESP_ERROR_CHECK(esp_himem_unmap(rh, ptr, ESP_HIMEM_BLKSZ));
|
||||
}
|
||||
vTaskDelay(5); //give the OS some time to do things so the task watchdog doesn't bark
|
||||
for (int i = 0; i < check_size; i += ESP_HIMEM_BLKSZ) {
|
||||
uint32_t *ptr;
|
||||
//Map in block, check against earlier written pseudo-random data, unmap block.
|
||||
ESP_ERROR_CHECK(esp_himem_map(mh, rh, i, 0, ESP_HIMEM_BLKSZ, 0, (void**)&ptr));
|
||||
if (!check_mem_seed(i ^ seed, ptr, ESP_HIMEM_BLKSZ, i)) {
|
||||
//printf("Error in block %d\n", i / ESP_HIMEM_BLKSZ);
|
||||
ret = false;
|
||||
}
|
||||
ESP_ERROR_CHECK(esp_himem_unmap(rh, ptr, ESP_HIMEM_BLKSZ));
|
||||
if (!ret) break; //don't check rest of blocks if error occurred
|
||||
}
|
||||
//Okay, all done!
|
||||
ESP_ERROR_CHECK(esp_himem_free(mh));
|
||||
ESP_ERROR_CHECK(esp_himem_free_map_range(rh));
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
std::string himem_memory_check()
|
||||
{
|
||||
size_t memcnt=esp_himem_get_phys_size();
|
||||
size_t memfree=esp_himem_get_free_size();
|
||||
|
||||
std::string espInfoResultStr = "";
|
||||
char aMsgBuf[40];
|
||||
|
||||
espInfoResultStr += "Running HIMEM memory check";
|
||||
|
||||
sprintf(aMsgBuf,"Himem has %dKiB of memory", (int)memcnt/1024);
|
||||
espInfoResultStr += std::string(aMsgBuf);
|
||||
|
||||
sprintf(aMsgBuf,"%dKiB of which is free", (int)memfree/1024);
|
||||
espInfoResultStr += std::string(aMsgBuf);
|
||||
|
||||
espInfoResultStr += "\n please wait ....\n";
|
||||
|
||||
//running memory checks
|
||||
//assert(test_region(memfree, 0xaaaa));
|
||||
|
||||
if(test_region(memfree, 0xaaaa)) {
|
||||
espInfoResultStr += "Himem check Failed!\n";
|
||||
} else {
|
||||
espInfoResultStr += "Himem check Done!\n";
|
||||
}
|
||||
|
||||
return espInfoResultStr;
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif // DEBUG_HIMEM_MEMORY_CHECK
|
||||
@@ -1,41 +0,0 @@
|
||||
|
||||
// need [env:esp32cam-dev-himem]
|
||||
//CONFIG_SPIRAM_BANKSWITCH_ENABLE=y
|
||||
//CONFIG_SPIRAM_BANKSWITCH_RESERVE=4
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../include/defines.h"
|
||||
|
||||
#ifdef DEBUG_HIMEM_MEMORY_CHECK
|
||||
|
||||
#ifndef HIMEM_MEMORY_CHECK_H
|
||||
#define HIMEM_MEMORY_CHECK_H
|
||||
|
||||
|
||||
|
||||
//source : //source : https://github.com/espressif/esp-idf/blob/master/examples/system/himem/main/himem_example_main.c
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp32/himem.h"
|
||||
|
||||
#include <string>
|
||||
#include "esp32/himem.h"
|
||||
|
||||
|
||||
std::string himem_memory_check();
|
||||
|
||||
#endif //HIMEM_MEMORY_CHECK_H
|
||||
|
||||
#endif // DEBUG_HIMEM_MEMORY_CHECK
|
||||
@@ -1,87 +0,0 @@
|
||||
//source : https://github.com/Carbon225/esp32-perfmon
|
||||
|
||||
#include "../../include/defines.h"
|
||||
|
||||
/*
|
||||
ESP32 CPU usage monitor
|
||||
Gives you a rough idea of how the Xtensa cores are utilized.
|
||||
|
||||
Works by attaching idle hooks and measuring how often they get called. The core usage is calculated: usage% = idle ticks since last measurement / expected idle ticks if core were idle * 100%. The expected idle tick count was measured by running an empty program.
|
||||
|
||||
Limitations:
|
||||
Should only be used for user information, not in logic that needs accurate values
|
||||
New IDF versions could optimize performance and therefore introduce an error to usage estimation.
|
||||
When one core is at 100% the other might report a negative value
|
||||
|
||||
Usage:
|
||||
#include "perfmon.h"
|
||||
Call perfmon_start() once
|
||||
|
||||
*/
|
||||
|
||||
#ifdef DEBUG_ENABLE_PERFMON
|
||||
|
||||
#include "perfmon.h"
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_freertos_hooks.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#include "esp_log.h"
|
||||
static const char *TAG = "perfmon";
|
||||
|
||||
static uint64_t idle0Calls = 0;
|
||||
static uint64_t idle1Calls = 0;
|
||||
|
||||
#if defined(CONFIG_ESP32_DEFAULT_CPU_FREQ_240)
|
||||
static const uint64_t MaxIdleCalls = 1855000;
|
||||
#elif defined(CONFIG_ESP32_DEFAULT_CPU_FREQ_160)
|
||||
static const uint64_t MaxIdleCalls = 1233100;
|
||||
#else
|
||||
#error "Unsupported CPU frequency"
|
||||
#endif
|
||||
|
||||
static bool idle_task_0()
|
||||
{
|
||||
idle0Calls += 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool idle_task_1()
|
||||
{
|
||||
idle1Calls += 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
static void perfmon_task(void *args)
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
float idle0 = idle0Calls;
|
||||
float idle1 = idle1Calls;
|
||||
idle0Calls = 0;
|
||||
idle1Calls = 0;
|
||||
|
||||
int cpu0 = 100.f - idle0 / MaxIdleCalls * 100.f;
|
||||
int cpu1 = 100.f - idle1 / MaxIdleCalls * 100.f;
|
||||
|
||||
ESP_LOGI(TAG, "Core 0 at %d%%", cpu0);
|
||||
ESP_LOGI(TAG, "Core 1 at %d%%", cpu1);
|
||||
// TODO configurable delay
|
||||
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
esp_err_t perfmon_start()
|
||||
{
|
||||
ESP_ERROR_CHECK(esp_register_freertos_idle_hook_for_cpu(idle_task_0, 0));
|
||||
ESP_ERROR_CHECK(esp_register_freertos_idle_hook_for_cpu(idle_task_1, 1));
|
||||
// TODO calculate optimal stack size
|
||||
xTaskCreate(perfmon_task, "perfmon", 2048, NULL, 1, NULL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
#endif // DEBUG_ENABLE_PERFMON
|
||||
@@ -1,24 +0,0 @@
|
||||
|
||||
#include "../../include/defines.h"
|
||||
|
||||
#ifdef DEBUG_ENABLE_PERFMON
|
||||
|
||||
#ifndef COMPONENTS_PERFMON_INCLUDE_PERFMON_H_
|
||||
#define COMPONENTS_PERFMON_INCLUDE_PERFMON_H_
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "esp_err.h"
|
||||
|
||||
esp_err_t perfmon_start();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* COMPONENTS_PERFMON_INCLUDE_PERFMON_H_ */
|
||||
|
||||
#endif //DEBUG_ENABLE_PERFMON
|
||||
@@ -1,41 +1,41 @@
|
||||
#include "defines.h"
|
||||
|
||||
#include "ClassLogFile.h"
|
||||
#include "../../include/defines.h"
|
||||
#include "psram.h"
|
||||
|
||||
static const char* TAG = "PSRAM";
|
||||
static const char *TAG = "PSRAM";
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
void *shared_region = NULL;
|
||||
uint32_t allocatedBytesForSTBI = 0;
|
||||
std::string sharedMemoryInUseFor = "";
|
||||
|
||||
|
||||
/** Reserve a large block in the PSRAM which will be shared between the different steps.
|
||||
* Each step uses it differently but only wiuthin itself. */
|
||||
bool reserve_psram_shared_region(void) {
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Allocating shared PSRAM region (" +
|
||||
std::to_string(TENSOR_ARENA_SIZE + MAX_MODEL_SIZE) + " bytes)...");
|
||||
shared_region = malloc_psram_heap("Shared PSRAM region", TENSOR_ARENA_SIZE + MAX_MODEL_SIZE,
|
||||
MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
bool reserve_psram_shared_region(void)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Allocating shared PSRAM region (" + std::to_string(TENSOR_ARENA_SIZE + MAX_MODEL_SIZE) + " bytes)...");
|
||||
shared_region = malloc_psram_heap("Shared PSRAM region", TENSOR_ARENA_SIZE + MAX_MODEL_SIZE, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
|
||||
if (shared_region == NULL) {
|
||||
if (shared_region == NULL)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to allocating shared PSRAM region!");
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************
|
||||
* Memory used in Take Image (STBI)
|
||||
*******************************************************************/
|
||||
bool psram_init_shared_memory_for_take_image_step(void) {
|
||||
if (sharedMemoryInUseFor != "") {
|
||||
bool psram_init_shared_memory_for_take_image_step(void)
|
||||
{
|
||||
if (sharedMemoryInUseFor != "")
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Shared memory in PSRAM already in use for " + sharedMemoryInUseFor + "!");
|
||||
return false;
|
||||
}
|
||||
@@ -47,21 +47,50 @@ bool psram_init_shared_memory_for_take_image_step(void) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void psram_deinit_shared_memory_for_take_image_step(void) {
|
||||
void psram_deinit_shared_memory_for_take_image_step(void)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Deinit shared memory for step 'Take Image' (STBI buffers)");
|
||||
allocatedBytesForSTBI = 0;
|
||||
sharedMemoryInUseFor = "";
|
||||
}
|
||||
|
||||
/*******************************************************************
|
||||
* Memory used in Aligning Step
|
||||
* During this step we only use the shared part of the PSRAM
|
||||
* for the TempImage.
|
||||
*******************************************************************/
|
||||
void *psram_reserve_shared_tmp_image_memory(void)
|
||||
{
|
||||
if (sharedMemoryInUseFor != "")
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Shared memory in PSRAM already in use for " + sharedMemoryInUseFor + "!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *psram_reserve_shared_stbi_memory(size_t size) {
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Allocating TempImage (" + std::to_string(IMAGE_SIZE) + " bytes, use shared memory in PSRAM)...");
|
||||
sharedMemoryInUseFor = "TempImage";
|
||||
return shared_region; // Use 1th part of the shared memory for the TempImage (only user)
|
||||
}
|
||||
|
||||
void psram_free_shared_temp_image_memory(void)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Shared memory used for TempImage (PSRAM, part of shared memory) is free again");
|
||||
sharedMemoryInUseFor = "";
|
||||
}
|
||||
|
||||
/*******************************************************************
|
||||
*
|
||||
*******************************************************************/
|
||||
void *psram_reserve_shared_stbi_memory(size_t size)
|
||||
{
|
||||
/* Only large buffers should be placed in the shared PSRAM
|
||||
* If we also place all smaller STBI buffers here, we get artefacts for some reasons. */
|
||||
if (size >= 100000) {
|
||||
if ((allocatedBytesForSTBI + size) > TENSOR_ARENA_SIZE + MAX_MODEL_SIZE) { // Check if it still fits in the shared region
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Shared memory in PSRAM too small (STBI) to fit additional " +
|
||||
std::to_string(size) + " bytes! Available: " + std::to_string(TENSOR_ARENA_SIZE + MAX_MODEL_SIZE - allocatedBytesForSTBI) + " bytes!");
|
||||
if (size >= 100000)
|
||||
{
|
||||
if ((allocatedBytesForSTBI + size) > TENSOR_ARENA_SIZE + MAX_MODEL_SIZE)
|
||||
{
|
||||
// Check if it still fits in the shared region
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Shared memory in PSRAM too small (STBI) to fit additional " + std::to_string(size) + " bytes! Available: " + std::to_string(TENSOR_ARENA_SIZE + MAX_MODEL_SIZE - allocatedBytesForSTBI) + " bytes!");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -70,55 +99,35 @@ void *psram_reserve_shared_stbi_memory(size_t size) {
|
||||
allocatedBytesForSTBI += size;
|
||||
return (uint8_t *)shared_region + allocatedBytesForSTBI - size;
|
||||
}
|
||||
else { // Normal PSRAM
|
||||
else
|
||||
{
|
||||
// Normal PSRAM
|
||||
return malloc_psram_heap("STBI", size, MALLOC_CAP_SPIRAM);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void *psram_reallocate_shared_stbi_memory(void *ptr, size_t newsize) {
|
||||
void *psram_reallocate_shared_stbi_memory(void *ptr, size_t newsize)
|
||||
{
|
||||
char buf[20];
|
||||
sprintf(buf, "%p", ptr);
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "STBI requested realloc for " + std::string(buf) + " but this is currently unsupported!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
void psram_free_shared_stbi_memory(void *p) {
|
||||
if ((p >= shared_region) && (p <= ((uint8_t *)shared_region + allocatedBytesForSTBI))) { // was allocated inside the shared memory
|
||||
void psram_free_shared_stbi_memory(void *p)
|
||||
{
|
||||
if ((p >= shared_region) && (p <= ((uint8_t *)shared_region + allocatedBytesForSTBI)))
|
||||
{
|
||||
// was allocated inside the shared memory
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Part of shared memory used for STBI (PSRAM, part of shared memory) is free again");
|
||||
}
|
||||
else { // Normal PSRAM
|
||||
else
|
||||
{
|
||||
// Normal PSRAM
|
||||
free_psram_heap("STBI", p);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************
|
||||
* Memory used in Aligning Step
|
||||
* During this step we only use the shared part of the PSRAM
|
||||
* for the tmpImage.
|
||||
*******************************************************************/
|
||||
void *psram_reserve_shared_tmp_image_memory(void) {
|
||||
if (sharedMemoryInUseFor != "") {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Shared memory in PSRAM already in use for " + sharedMemoryInUseFor + "!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Allocating tmpImage (" + std::to_string(IMAGE_SIZE) + " bytes, use shared memory in PSRAM)...");
|
||||
sharedMemoryInUseFor = "Aligning";
|
||||
return shared_region; // Use 1th part of the shared memory for the tmpImage (only user)
|
||||
}
|
||||
|
||||
|
||||
void psram_free_shared_temp_image_memory(void) {
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Shared memory used for tmpImage (PSRAM, part of shared memory) is free again");
|
||||
sharedMemoryInUseFor = "";
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************
|
||||
* Memory used in Digitization Steps
|
||||
* During this step we only use the shared part of the PSRAM for the
|
||||
@@ -126,86 +135,96 @@ void psram_free_shared_temp_image_memory(void) {
|
||||
* The shared memory is large enough for the largest model and the
|
||||
* Tensor Arena. Therefore we do not need to monitor the usage.
|
||||
*******************************************************************/
|
||||
void *psram_get_shared_tensor_arena_memory(void) {
|
||||
if ((sharedMemoryInUseFor == "") || (sharedMemoryInUseFor == "Digitization_Model")) {
|
||||
void *psram_get_shared_tensor_arena_memory(void)
|
||||
{
|
||||
if ((sharedMemoryInUseFor == "") || (sharedMemoryInUseFor == "Digitization_Model"))
|
||||
{
|
||||
sharedMemoryInUseFor = "Digitization_Tensor";
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Allocating Tensor Arena (" + std::to_string(TENSOR_ARENA_SIZE) + " bytes, use shared memory in PSRAM)...");
|
||||
return shared_region; // Use 1th part of the shared memory for Tensor
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Shared memory in PSRAM already in use for " + sharedMemoryInUseFor + "!");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void *psram_get_shared_model_memory(void) {
|
||||
if ((sharedMemoryInUseFor == "") || (sharedMemoryInUseFor == "Digitization_Tensor")) {
|
||||
void *psram_get_shared_model_memory(void)
|
||||
{
|
||||
if ((sharedMemoryInUseFor == "") || (sharedMemoryInUseFor == "Digitization_Tensor"))
|
||||
{
|
||||
sharedMemoryInUseFor = "Digitization_Model";
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Allocating Model memory (" + std::to_string(MAX_MODEL_SIZE) + " bytes, use shared memory in PSRAM)...");
|
||||
return (uint8_t *)shared_region + TENSOR_ARENA_SIZE; // Use 2nd part of the shared memory (after Tensor Arena) for the model
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Shared memory in PSRAM already in use for " + sharedMemoryInUseFor + "!");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void psram_free_shared_tensor_arena_and_model_memory(void) {
|
||||
void psram_free_shared_tensor_arena_and_model_memory(void)
|
||||
{
|
||||
sharedMemoryInUseFor = "";
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Shared memory used for Tensor Arena and model (PSRAM, part of shared memory) is free again");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************
|
||||
* General
|
||||
*******************************************************************/
|
||||
void *malloc_psram_heap(std::string name, size_t size, uint32_t caps) {
|
||||
void *ptr;
|
||||
void *malloc_psram_heap(std::string name, size_t size, uint32_t caps)
|
||||
{
|
||||
void *ptr;
|
||||
|
||||
ptr = heap_caps_malloc(size, caps);
|
||||
if (ptr != NULL) {
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Allocated " + to_string(size) + " bytes in PSRAM for '" + name + "'");
|
||||
}
|
||||
else {
|
||||
ptr = heap_caps_malloc(size, caps);
|
||||
if (ptr != NULL)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Allocated " + to_string(size) + " bytes in PSRAM for '" + name + "'");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to allocate " + to_string(size) + " bytes in PSRAM for '" + name + "'!");
|
||||
}
|
||||
|
||||
return ptr;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
|
||||
void *realloc_psram_heap(std::string name, void *ptr, size_t size, uint32_t caps) {
|
||||
ptr = heap_caps_realloc(ptr, size, caps);
|
||||
if (ptr != NULL) {
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Reallocated " + to_string(size) + " bytes in PSRAM for '" + name + "'");
|
||||
}
|
||||
else {
|
||||
void *realloc_psram_heap(std::string name, void *ptr, size_t size, uint32_t caps)
|
||||
{
|
||||
ptr = heap_caps_realloc(ptr, size, caps);
|
||||
if (ptr != NULL)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Reallocated " + to_string(size) + " bytes in PSRAM for '" + name + "'");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to reallocate " + to_string(size) + " bytes in PSRAM for '" + name + "'!");
|
||||
}
|
||||
|
||||
return ptr;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void *calloc_psram_heap(std::string name, size_t n, size_t size, uint32_t caps)
|
||||
{
|
||||
void *ptr;
|
||||
|
||||
void *calloc_psram_heap(std::string name, size_t n, size_t size, uint32_t caps) {
|
||||
void *ptr;
|
||||
|
||||
ptr = heap_caps_calloc(n, size, caps);
|
||||
if (ptr != NULL) {
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Allocated " + to_string(size) + " bytes in PSRAM for '" + name + "'");
|
||||
}
|
||||
else {
|
||||
ptr = heap_caps_calloc(n, size, caps);
|
||||
if (ptr != NULL)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Allocated " + to_string(size) + " bytes in PSRAM for '" + name + "'");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to allocate " + to_string(size) + " bytes in PSRAM for '" + name + "'!");
|
||||
}
|
||||
|
||||
return ptr;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
|
||||
void free_psram_heap(std::string name, void *ptr) {
|
||||
void free_psram_heap(std::string name, void *ptr)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Freeing memory in PSRAM used for '" + name + "'...");
|
||||
heap_caps_free(ptr);
|
||||
}
|
||||
|
||||
@@ -4,10 +4,8 @@
|
||||
|
||||
#include "esp_heap_caps.h"
|
||||
|
||||
|
||||
bool reserve_psram_shared_region(void);
|
||||
|
||||
|
||||
/* Memory used in Take Image Step */
|
||||
bool psram_init_shared_memory_for_take_image_step(void);
|
||||
void psram_deinit_shared_memory_for_take_image_step(void);
|
||||
@@ -15,7 +13,6 @@ void *psram_reserve_shared_stbi_memory(size_t size);
|
||||
void *psram_reallocate_shared_stbi_memory(void *ptr, size_t newsize);
|
||||
void psram_free_shared_stbi_memory(void *p);
|
||||
|
||||
|
||||
/* Memory used in Aligning Step */
|
||||
void *psram_reserve_shared_tmp_image_memory(void);
|
||||
void psram_free_shared_temp_image_memory(void);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#ifndef COMPONENTS_HELPER_SDCARD_CHECK_H
|
||||
#define COMPONENTS_HELPER_SDCARD_CHECK_H
|
||||
|
||||
#include "../../include/defines.h"
|
||||
#include "defines.h"
|
||||
|
||||
int SDCardCheckRW(void);
|
||||
bool SDCardCheckFolderFilePresence(void);
|
||||
|
||||
@@ -1,646 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "sdcard_init.h"
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "ffconf.h"
|
||||
#include "esp_compiler.h"
|
||||
#include "esp_vfs.h"
|
||||
#include "vfs_fat_internal.h"
|
||||
#include "diskio_impl.h"
|
||||
#include "diskio_sdmmc.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "driver/sdmmc_defs.h"
|
||||
|
||||
#if SOC_SDMMC_HOST_SUPPORTED
|
||||
#include "driver/sdmmc_host.h"
|
||||
#endif
|
||||
|
||||
static sdmmc_card_t* s_cards[FF_VOLUMES] = { NULL };
|
||||
static bool s_disk_status_check_en[FF_VOLUMES] = { };
|
||||
|
||||
static const char* TAG = "sdcard_init";
|
||||
|
||||
#define CHECK_EXECUTE_RESULT(err, str) do { \
|
||||
if ((err) !=ESP_OK) { \
|
||||
ESP_LOGE(TAG, str" (0x%x).", err); \
|
||||
goto cleanup; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
typedef struct mh_vfs_fat_sd_ctx_t {
|
||||
BYTE pdrv; //Drive number that is mounted
|
||||
esp_vfs_fat_mount_config_t mount_config; //Mount configuration
|
||||
FATFS *fs; //FAT structure pointer that is registered
|
||||
sdmmc_card_t *card; //Card info
|
||||
char *base_path; //Path where partition is registered
|
||||
} mh_vfs_fat_sd_ctx_t;
|
||||
|
||||
static mh_vfs_fat_sd_ctx_t *s_ctx[FF_VOLUMES] = {};
|
||||
|
||||
/**
|
||||
* This `s_saved_ctx_id` is only used by `esp_vfs_fat_sdmmc_unmount`, which is deprecated.
|
||||
* This variable together with `esp_vfs_fat_sdmmc_unmount` should be removed in next major version
|
||||
*/
|
||||
static uint32_t s_saved_ctx_id = FF_VOLUMES;
|
||||
|
||||
|
||||
static void call_host_deinit_mh(const sdmmc_host_t *host_config);
|
||||
static esp_err_t partition_card_mh(const esp_vfs_fat_mount_config_t *mount_config, const char *drv, sdmmc_card_t *card, BYTE pdrv);
|
||||
|
||||
|
||||
//Check if SD/MMC card is present
|
||||
static DSTATUS ff_sdmmc_card_available_mh(BYTE pdrv)
|
||||
{
|
||||
sdmmc_card_t* card = s_cards[pdrv];
|
||||
assert(card);
|
||||
esp_err_t err = sdmmc_get_status(card);
|
||||
|
||||
if (unlikely(err != ESP_OK)) {
|
||||
ESP_LOGE(TAG, "Check status failed (0x%x)", err);
|
||||
return STA_NOINIT;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ff_sdmmc_status() and ff_sdmmc_initialize() return STA_NOINIT when sdmmc_get_status()
|
||||
* fails. This error value is checked throughout the FATFS code.
|
||||
* Both functions return 0 on success.
|
||||
*/
|
||||
DSTATUS ff_sdmmc_initialize_mh (BYTE pdrv)
|
||||
{
|
||||
return ff_sdmmc_card_available_mh(pdrv);
|
||||
}
|
||||
|
||||
DSTATUS ff_sdmmc_status_mh(BYTE pdrv)
|
||||
{
|
||||
if (s_disk_status_check_en[pdrv]) {
|
||||
return ff_sdmmc_card_available_mh(pdrv);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
DRESULT ff_sdmmc_read_mh (BYTE pdrv, BYTE* buff, DWORD sector, UINT count)
|
||||
{
|
||||
sdmmc_card_t* card = s_cards[pdrv];
|
||||
assert(card);
|
||||
esp_err_t err = sdmmc_read_sectors(card, buff, sector, count);
|
||||
if (unlikely(err != ESP_OK)) {
|
||||
ESP_LOGE(TAG, "sdmmc_read_blocks failed (%d)", err);
|
||||
return RES_ERROR;
|
||||
}
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
DRESULT ff_sdmmc_write_mh (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count)
|
||||
{
|
||||
sdmmc_card_t* card = s_cards[pdrv];
|
||||
assert(card);
|
||||
esp_err_t err = sdmmc_write_sectors(card, buff, sector, count);
|
||||
if (unlikely(err != ESP_OK)) {
|
||||
ESP_LOGE(TAG, "sdmmc_write_blocks failed (%d)", err);
|
||||
return RES_ERROR;
|
||||
}
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
#if FF_USE_TRIM
|
||||
DRESULT ff_sdmmc_trim_mh (BYTE pdrv, DWORD start_sector, DWORD sector_count)
|
||||
{
|
||||
sdmmc_card_t* card = s_cards[pdrv];
|
||||
assert(card);
|
||||
sdmmc_erase_arg_t arg;
|
||||
|
||||
arg = sdmmc_can_discard(card) == ESP_OK ? SDMMC_DISCARD_ARG : SDMMC_ERASE_ARG;
|
||||
esp_err_t err = sdmmc_erase_sectors(card, start_sector, sector_count, arg);
|
||||
if (unlikely(err != ESP_OK)) {
|
||||
ESP_LOGE(TAG, "sdmmc_erase_sectors failed (%d)", err);
|
||||
return RES_ERROR;
|
||||
}
|
||||
return RES_OK;
|
||||
}
|
||||
#endif //FF_USE_TRIM
|
||||
|
||||
DRESULT ff_sdmmc_ioctl_mh (BYTE pdrv, BYTE cmd, void* buff)
|
||||
{
|
||||
sdmmc_card_t* card = s_cards[pdrv];
|
||||
assert(card);
|
||||
switch(cmd) {
|
||||
case CTRL_SYNC:
|
||||
return RES_OK;
|
||||
case GET_SECTOR_COUNT:
|
||||
*((DWORD*) buff) = card->csd.capacity;
|
||||
return RES_OK;
|
||||
case GET_SECTOR_SIZE:
|
||||
*((WORD*) buff) = card->csd.sector_size;
|
||||
return RES_OK;
|
||||
case GET_BLOCK_SIZE:
|
||||
return RES_ERROR;
|
||||
#if FF_USE_TRIM
|
||||
case CTRL_TRIM:
|
||||
if (sdmmc_can_trim(card) != ESP_OK) {
|
||||
return RES_PARERR;
|
||||
}
|
||||
return ff_sdmmc_trim_mh (pdrv, *((DWORD*)buff), //start_sector
|
||||
(*((DWORD*)buff + 1) - *((DWORD*)buff) + 1)); //sector_count
|
||||
#endif //FF_USE_TRIM
|
||||
}
|
||||
return RES_ERROR;
|
||||
}
|
||||
|
||||
void ff_sdmmc_set_disk_status_check_mh(BYTE pdrv, bool enable)
|
||||
{
|
||||
s_disk_status_check_en[pdrv] = enable;
|
||||
}
|
||||
|
||||
void ff_diskio_register_sdmmc_mh(BYTE pdrv, sdmmc_card_t* card)
|
||||
{
|
||||
static const ff_diskio_impl_t sdmmc_impl = {
|
||||
.init = &ff_sdmmc_initialize_mh,
|
||||
.status = &ff_sdmmc_status_mh,
|
||||
.read = &ff_sdmmc_read_mh,
|
||||
.write = &ff_sdmmc_write_mh,
|
||||
.ioctl = &ff_sdmmc_ioctl_mh
|
||||
};
|
||||
s_cards[pdrv] = card;
|
||||
s_disk_status_check_en[pdrv] = false;
|
||||
ff_diskio_register(pdrv, &sdmmc_impl);
|
||||
}
|
||||
|
||||
BYTE ff_diskio_get_pdrv_card_mh(const sdmmc_card_t* card)
|
||||
{
|
||||
for (int i = 0; i < FF_VOLUMES; i++) {
|
||||
if (card == s_cards[i]) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
static bool s_get_context_id_by_card_mh(const sdmmc_card_t *card, uint32_t *out_id)
|
||||
{
|
||||
mh_vfs_fat_sd_ctx_t *p_ctx = NULL;
|
||||
for (int i = 0; i < FF_VOLUMES; i++) {
|
||||
p_ctx = s_ctx[i];
|
||||
if (p_ctx) {
|
||||
if (p_ctx->card == card) {
|
||||
*out_id = i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static uint32_t s_get_unused_context_id_mh(void)
|
||||
{
|
||||
for (uint32_t i = 0; i < FF_VOLUMES; i++) {
|
||||
if (!s_ctx[i]) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return FF_VOLUMES;
|
||||
}
|
||||
|
||||
static esp_err_t mount_prepare_mem_mh(const char *base_path, BYTE *out_pdrv, char **out_dup_path, sdmmc_card_t** out_card)
|
||||
{
|
||||
esp_err_t err = ESP_OK;
|
||||
char* dup_path = NULL;
|
||||
sdmmc_card_t* card = NULL;
|
||||
|
||||
// connect SDMMC driver to FATFS
|
||||
BYTE pdrv = FF_DRV_NOT_USED;
|
||||
|
||||
if (ff_diskio_get_drive(&pdrv) != ESP_OK || pdrv == FF_DRV_NOT_USED) {
|
||||
ESP_LOGD(TAG, "the maximum count of volumes is already mounted");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
// not using ff_memalloc here, as allocation in internal RAM is preferred
|
||||
card = (sdmmc_card_t*)malloc(sizeof(sdmmc_card_t));
|
||||
|
||||
if (card == NULL) {
|
||||
ESP_LOGD(TAG, "could not locate new sdmmc_card_t");
|
||||
err = ESP_ERR_NO_MEM;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
dup_path = strdup(base_path);
|
||||
|
||||
if(!dup_path){
|
||||
ESP_LOGD(TAG, "could not copy base_path");
|
||||
err = ESP_ERR_NO_MEM;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
*out_card = card;
|
||||
*out_pdrv = pdrv;
|
||||
*out_dup_path = dup_path;
|
||||
return ESP_OK;
|
||||
cleanup:
|
||||
free(card);
|
||||
free(dup_path);
|
||||
return err;
|
||||
}
|
||||
|
||||
static esp_err_t s_f_mount_mh(sdmmc_card_t *card, FATFS *fs, const char *drv, uint8_t pdrv, const esp_vfs_fat_mount_config_t *mount_config)
|
||||
{
|
||||
esp_err_t err = ESP_OK;
|
||||
FRESULT res = f_mount(fs, drv, 1);
|
||||
if (res != FR_OK) {
|
||||
err = ESP_FAIL;
|
||||
ESP_LOGW(TAG, "failed to mount card (%d)", res);
|
||||
|
||||
bool need_mount_again = (res == FR_NO_FILESYSTEM || res == FR_INT_ERR) && mount_config->format_if_mount_failed;
|
||||
|
||||
if (!need_mount_again) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
err = partition_card_mh(mount_config, drv, card, pdrv);
|
||||
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
ESP_LOGW(TAG, "mounting again");
|
||||
res = f_mount(fs, drv, 0);
|
||||
|
||||
if (res != FR_OK) {
|
||||
err = ESP_FAIL;
|
||||
ESP_LOGD(TAG, "f_mount failed after formatting (%d)", res);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t mount_to_vfs_fat_mh(const esp_vfs_fat_mount_config_t *mount_config, sdmmc_card_t *card, uint8_t pdrv, const char *base_path, FATFS **out_fs)
|
||||
{
|
||||
FATFS *fs = NULL;
|
||||
esp_err_t err;
|
||||
ff_diskio_register_sdmmc_mh(pdrv, card);
|
||||
ff_sdmmc_set_disk_status_check_mh(pdrv, mount_config->disk_status_check_enable);
|
||||
ESP_LOGD(TAG, "using pdrv=%i", pdrv);
|
||||
char drv[3] = {(char)('0' + pdrv), ':', 0};
|
||||
|
||||
// connect FATFS to VFS
|
||||
err = esp_vfs_fat_register(base_path, drv, mount_config->max_files, &fs);
|
||||
*out_fs = fs;
|
||||
|
||||
if (err == ESP_ERR_INVALID_STATE) {
|
||||
// it's okay, already registered with VFS
|
||||
} else if (err != ESP_OK) {
|
||||
ESP_LOGD(TAG, "esp_vfs_fat_register failed 0x(%x)", err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// Try to mount partition
|
||||
err = s_f_mount_mh(card, fs, drv, pdrv, mount_config);
|
||||
|
||||
if (err != ESP_OK) {
|
||||
goto fail;
|
||||
}
|
||||
return ESP_OK;
|
||||
|
||||
fail:
|
||||
if (fs) {
|
||||
f_mount(NULL, drv, 0);
|
||||
}
|
||||
esp_vfs_fat_unregister_path(base_path);
|
||||
ff_diskio_unregister(pdrv);
|
||||
return err;
|
||||
}
|
||||
|
||||
static esp_err_t partition_card_mh(const esp_vfs_fat_mount_config_t *mount_config, const char *drv, sdmmc_card_t *card, BYTE pdrv)
|
||||
{
|
||||
FRESULT res = FR_OK;
|
||||
esp_err_t err;
|
||||
const size_t workbuf_size = 4096;
|
||||
void* workbuf = NULL;
|
||||
ESP_LOGW(TAG, "partitioning card");
|
||||
|
||||
workbuf = ff_memalloc(workbuf_size);
|
||||
|
||||
if (workbuf == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
LBA_t plist[] = {100, 0, 0, 0};
|
||||
res = f_fdisk(pdrv, plist, workbuf);
|
||||
|
||||
if (res != FR_OK) {
|
||||
err = ESP_FAIL;
|
||||
ESP_LOGD(TAG, "f_fdisk failed (%d)", res);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
size_t alloc_unit_size = esp_vfs_fat_get_allocation_unit_size(card->csd.sector_size, mount_config->allocation_unit_size);
|
||||
|
||||
ESP_LOGW(TAG, "formatting card, allocation unit size=%d", alloc_unit_size);
|
||||
const MKFS_PARM opt = {(BYTE)FM_ANY, 0, 0, 0, alloc_unit_size};
|
||||
res = f_mkfs(drv, &opt, workbuf, workbuf_size);
|
||||
|
||||
if (res != FR_OK) {
|
||||
err = ESP_FAIL;
|
||||
ESP_LOGD(TAG, "f_mkfs failed (%d)", res);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
free(workbuf);
|
||||
return ESP_OK;
|
||||
fail:
|
||||
free(workbuf);
|
||||
return err;
|
||||
}
|
||||
|
||||
#if SOC_SDMMC_HOST_SUPPORTED
|
||||
static esp_err_t init_sdmmc_host_mh(int slot, const void *slot_config, int *out_slot)
|
||||
{
|
||||
*out_slot = slot;
|
||||
return sdmmc_host_init_slot(slot, (const sdmmc_slot_config_t*) slot_config);
|
||||
}
|
||||
|
||||
esp_err_t esp_vfs_fat_sdmmc_mount_mh(const char* base_path, const sdmmc_host_t* host_config, const void* slot_config, const esp_vfs_fat_mount_config_t* mount_config, sdmmc_card_t** out_card)
|
||||
{
|
||||
esp_err_t err;
|
||||
mh_vfs_fat_sd_ctx_t *ctx = NULL;
|
||||
uint32_t ctx_id = FF_VOLUMES;
|
||||
FATFS *fs = NULL;
|
||||
int card_handle = -1; //uninitialized
|
||||
sdmmc_card_t* card = NULL;
|
||||
BYTE pdrv = FF_DRV_NOT_USED;
|
||||
char* dup_path = NULL;
|
||||
bool host_inited = false;
|
||||
|
||||
err = mount_prepare_mem_mh(base_path, &pdrv, &dup_path, &card);
|
||||
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "mount_prepare failed");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = (*host_config->init)();
|
||||
CHECK_EXECUTE_RESULT(err, "host init failed");
|
||||
//deinit() needs to be called to revert the init
|
||||
host_inited = true;
|
||||
//If this failed (indicated by card_handle != -1), slot deinit needs to called()
|
||||
//leave card_handle as is to indicate that (though slot deinit not implemented yet.
|
||||
err = init_sdmmc_host_mh(host_config->slot, slot_config, &card_handle);
|
||||
CHECK_EXECUTE_RESULT(err, "slot init failed");
|
||||
|
||||
// probe and initialize card
|
||||
err = sdmmc_card_init(host_config, card);
|
||||
CHECK_EXECUTE_RESULT(err, "sdmmc_card_init failed");
|
||||
|
||||
err = mount_to_vfs_fat_mh(mount_config, card, pdrv, dup_path, &fs);
|
||||
CHECK_EXECUTE_RESULT(err, "mount_to_vfs failed");
|
||||
|
||||
if (out_card != NULL) {
|
||||
*out_card = card;
|
||||
}
|
||||
|
||||
//For deprecation backward compatibility
|
||||
if (s_saved_ctx_id == FF_VOLUMES) {
|
||||
s_saved_ctx_id = 0;
|
||||
}
|
||||
|
||||
ctx = calloc(sizeof(mh_vfs_fat_sd_ctx_t), 1);
|
||||
|
||||
if (!ctx) {
|
||||
CHECK_EXECUTE_RESULT(ESP_ERR_NO_MEM, "no mem");
|
||||
}
|
||||
|
||||
ctx->pdrv = pdrv;
|
||||
memcpy(&ctx->mount_config, mount_config, sizeof(esp_vfs_fat_mount_config_t));
|
||||
ctx->card = card;
|
||||
ctx->base_path = dup_path;
|
||||
ctx->fs = fs;
|
||||
ctx_id = s_get_unused_context_id_mh();
|
||||
assert(ctx_id != FF_VOLUMES);
|
||||
s_ctx[ctx_id] = ctx;
|
||||
|
||||
return ESP_OK;
|
||||
cleanup:
|
||||
if (host_inited) {
|
||||
call_host_deinit_mh(host_config);
|
||||
}
|
||||
|
||||
free(card);
|
||||
free(dup_path);
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
||||
static esp_err_t init_sdspi_host_mh(int slot, const void *slot_config, int *out_slot)
|
||||
{
|
||||
esp_err_t err = sdspi_host_init_device((const sdspi_device_config_t*)slot_config, out_slot);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG,
|
||||
"Failed to attach sdspi device onto an SPI bus (rc=0x%x), please initialize the \
|
||||
bus first and check the device parameters."
|
||||
, err);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t esp_vfs_fat_sdspi_mount_mh(const char* base_path, const sdmmc_host_t* host_config_input, const sdspi_device_config_t* slot_config, const esp_vfs_fat_mount_config_t* mount_config, sdmmc_card_t** out_card)
|
||||
{
|
||||
const sdmmc_host_t* host_config = host_config_input;
|
||||
esp_err_t err;
|
||||
mh_vfs_fat_sd_ctx_t *ctx = NULL;
|
||||
uint32_t ctx_id = FF_VOLUMES;
|
||||
FATFS *fs = NULL;
|
||||
int card_handle = -1; //uninitialized
|
||||
bool host_inited = false;
|
||||
BYTE pdrv = FF_DRV_NOT_USED;
|
||||
sdmmc_card_t* card = NULL;
|
||||
char* dup_path = NULL;
|
||||
|
||||
err = mount_prepare_mem_mh(base_path, &pdrv, &dup_path, &card);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "mount_prepare failed");
|
||||
return err;
|
||||
}
|
||||
|
||||
//the init() function is usually empty, doesn't require any deinit to revert it
|
||||
err = (*host_config->init)();
|
||||
CHECK_EXECUTE_RESULT(err, "host init failed");
|
||||
|
||||
err = init_sdspi_host_mh(host_config->slot, slot_config, &card_handle);
|
||||
CHECK_EXECUTE_RESULT(err, "slot init failed");
|
||||
//Set `host_inited` to true to indicate that host_config->deinit() needs
|
||||
//to be called to revert `init_sdspi_host`
|
||||
host_inited = true;
|
||||
|
||||
//The `slot` argument inside host_config should be replaced by the SD SPI handled returned
|
||||
//above. But the input pointer is const, so create a new variable.
|
||||
|
||||
sdmmc_host_t new_config;
|
||||
|
||||
if (card_handle != host_config->slot) {
|
||||
new_config = *host_config_input;
|
||||
host_config = &new_config;
|
||||
new_config.slot = card_handle;
|
||||
}
|
||||
|
||||
// probe and initialize card
|
||||
err = sdmmc_card_init(host_config, card);
|
||||
CHECK_EXECUTE_RESULT(err, "sdmmc_card_init failed");
|
||||
|
||||
err = mount_to_vfs_fat_mh(mount_config, card, pdrv, dup_path, &fs);
|
||||
CHECK_EXECUTE_RESULT(err, "mount_to_vfs failed");
|
||||
|
||||
if (out_card != NULL) {
|
||||
*out_card = card;
|
||||
}
|
||||
|
||||
//For deprecation backward compatibility
|
||||
if (s_saved_ctx_id == FF_VOLUMES) {
|
||||
s_saved_ctx_id = 0;
|
||||
}
|
||||
|
||||
ctx = calloc(sizeof(mh_vfs_fat_sd_ctx_t), 1);
|
||||
|
||||
if (!ctx) {
|
||||
CHECK_EXECUTE_RESULT(ESP_ERR_NO_MEM, "no mem");
|
||||
}
|
||||
|
||||
ctx->pdrv = pdrv;
|
||||
memcpy(&ctx->mount_config, mount_config, sizeof(esp_vfs_fat_mount_config_t));
|
||||
ctx->card = card;
|
||||
ctx->base_path = dup_path;
|
||||
ctx->fs = fs;
|
||||
ctx_id = s_get_unused_context_id_mh();
|
||||
assert(ctx_id != FF_VOLUMES);
|
||||
s_ctx[ctx_id] = ctx;
|
||||
|
||||
return ESP_OK;
|
||||
|
||||
cleanup:
|
||||
if (host_inited) {
|
||||
call_host_deinit_mh(host_config);
|
||||
}
|
||||
|
||||
free(card);
|
||||
free(dup_path);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void call_host_deinit_mh(const sdmmc_host_t *host_config)
|
||||
{
|
||||
if (host_config->flags & SDMMC_HOST_FLAG_DEINIT_ARG) {
|
||||
host_config->deinit_p(host_config->slot);
|
||||
} else {
|
||||
host_config->deinit();
|
||||
}
|
||||
}
|
||||
|
||||
static esp_err_t unmount_card_core_mh(const char *base_path, sdmmc_card_t *card)
|
||||
{
|
||||
BYTE pdrv = ff_diskio_get_pdrv_card_mh(card);
|
||||
|
||||
if (pdrv == 0xff) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
// unmount
|
||||
char drv[3] = {(char)('0' + pdrv), ':', 0};
|
||||
f_mount(0, drv, 0);
|
||||
// release SD driver
|
||||
ff_diskio_unregister(pdrv);
|
||||
|
||||
call_host_deinit_mh(&card->host);
|
||||
free(card);
|
||||
|
||||
esp_err_t err = esp_vfs_fat_unregister_path(base_path);
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t esp_vfs_fat_sdmmc_unmount_mh(void)
|
||||
{
|
||||
esp_err_t err = unmount_card_core_mh(s_ctx[s_saved_ctx_id]->base_path, s_ctx[s_saved_ctx_id]->card);
|
||||
free(s_ctx[s_saved_ctx_id]);
|
||||
s_ctx[s_saved_ctx_id] = NULL;
|
||||
s_saved_ctx_id = FF_VOLUMES;
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t esp_vfs_fat_sdcard_unmount_mh(const char *base_path, sdmmc_card_t *card)
|
||||
{
|
||||
uint32_t id = FF_VOLUMES;
|
||||
bool found = s_get_context_id_by_card_mh(card, &id);
|
||||
|
||||
if (!found) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
free(s_ctx[id]);
|
||||
s_ctx[id] = NULL;
|
||||
|
||||
esp_err_t err = unmount_card_core_mh(base_path, card);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t esp_vfs_fat_sdcard_format_mh(const char *base_path, sdmmc_card_t *card)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
if (!card) {
|
||||
ESP_LOGE(TAG, "card not initialized");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
BYTE pdrv = ff_diskio_get_pdrv_card_mh(card);
|
||||
|
||||
if (pdrv == 0xff) {
|
||||
ESP_LOGE(TAG, "card driver not registered");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
const size_t workbuf_size = 4096;
|
||||
void *workbuf = ff_memalloc(workbuf_size);
|
||||
|
||||
if (workbuf == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
//unmount
|
||||
char drv[3] = {(char)('0' + pdrv), ':', 0};
|
||||
f_mount(0, drv, 0);
|
||||
|
||||
//format
|
||||
uint32_t id = FF_VOLUMES;
|
||||
bool found = s_get_context_id_by_card_mh(card, &id);
|
||||
assert(found);
|
||||
size_t alloc_unit_size = esp_vfs_fat_get_allocation_unit_size(card->csd.sector_size, s_ctx[id]->mount_config.allocation_unit_size);
|
||||
ESP_LOGI(TAG, "Formatting card, allocation unit size=%d", alloc_unit_size);
|
||||
const MKFS_PARM opt = {(BYTE)FM_ANY, 0, 0, 0, alloc_unit_size};
|
||||
FRESULT res = f_mkfs(drv, &opt, workbuf, workbuf_size);
|
||||
free(workbuf);
|
||||
|
||||
if (res != FR_OK) {
|
||||
ret = ESP_FAIL;
|
||||
ESP_LOGD(TAG, "f_mkfs failed (%d)", res);
|
||||
}
|
||||
|
||||
//mount back
|
||||
esp_err_t err = s_f_mount_mh(card, s_ctx[id]->fs, drv, pdrv, &s_ctx[id]->mount_config);
|
||||
|
||||
if (err != ESP_OK) {
|
||||
unmount_card_core_mh(base_path, card);
|
||||
ESP_LOGE(TAG, "failed to format, resources recycled, please mount again");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include "esp_err.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "sdmmc_cmd.h"
|
||||
#include "driver/sdmmc_types.h"
|
||||
#include "driver/sdspi_host.h"
|
||||
#include "ff.h"
|
||||
#include "esp_vfs_fat.h"
|
||||
#include "wear_levelling.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Convenience function to get FAT filesystem on SD card registered in VFS
|
||||
*
|
||||
* This is an all-in-one function which does the following:
|
||||
* - initializes SDMMC driver or SPI driver with configuration in host_config
|
||||
* - initializes SD card with configuration in slot_config
|
||||
* - mounts FAT partition on SD card using FATFS library, with configuration in mount_config
|
||||
* - registers FATFS library with VFS, with prefix given by base_prefix variable
|
||||
*
|
||||
* This function is intended to make example code more compact.
|
||||
* For real world applications, developers should implement the logic of
|
||||
* probing SD card, locating and mounting partition, and registering FATFS in VFS,
|
||||
* with proper error checking and handling of exceptional conditions.
|
||||
*
|
||||
* @note Use this API to mount a card through SDSPI is deprecated. Please call
|
||||
* `esp_vfs_fat_sdspi_mount()` instead for that case.
|
||||
*
|
||||
* @param base_path path where partition should be registered (e.g. "/sdcard")
|
||||
* @param host_config Pointer to structure describing SDMMC host. When using
|
||||
* SDMMC peripheral, this structure can be initialized using
|
||||
* SDMMC_HOST_DEFAULT() macro. When using SPI peripheral,
|
||||
* this structure can be initialized using SDSPI_HOST_DEFAULT()
|
||||
* macro.
|
||||
* @param slot_config Pointer to structure with slot configuration.
|
||||
* For SDMMC peripheral, pass a pointer to sdmmc_slot_config_t
|
||||
* structure initialized using SDMMC_SLOT_CONFIG_DEFAULT.
|
||||
* @param mount_config pointer to structure with extra parameters for mounting FATFS
|
||||
* @param[out] out_card if not NULL, pointer to the card information structure will be returned via this argument
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_STATE if esp_vfs_fat_sdmmc_mount was already called
|
||||
* - ESP_ERR_NO_MEM if memory can not be allocated
|
||||
* - ESP_FAIL if partition can not be mounted
|
||||
* - other error codes from SDMMC or SPI drivers, SDMMC protocol, or FATFS drivers
|
||||
*/
|
||||
esp_err_t esp_vfs_fat_sdmmc_mount_mh(const char* base_path, const sdmmc_host_t* host_config, const void* slot_config, const esp_vfs_fat_mount_config_t* mount_config, sdmmc_card_t** out_card);
|
||||
|
||||
/**
|
||||
* @brief Convenience function to get FAT filesystem on SD card registered in VFS
|
||||
*
|
||||
* This is an all-in-one function which does the following:
|
||||
* - initializes an SPI Master device based on the SPI Master driver with configuration in
|
||||
* slot_config, and attach it to an initialized SPI bus.
|
||||
* - initializes SD card with configuration in host_config_input
|
||||
* - mounts FAT partition on SD card using FATFS library, with configuration in mount_config
|
||||
* - registers FATFS library with VFS, with prefix given by base_prefix variable
|
||||
*
|
||||
* This function is intended to make example code more compact.
|
||||
* For real world applications, developers should implement the logic of
|
||||
* probing SD card, locating and mounting partition, and registering FATFS in VFS,
|
||||
* with proper error checking and handling of exceptional conditions.
|
||||
*
|
||||
* @note This function try to attach the new SD SPI device to the bus specified in host_config.
|
||||
* Make sure the SPI bus specified in `host_config->slot` have been initialized by
|
||||
* `spi_bus_initialize()` before.
|
||||
*
|
||||
* @param base_path path where partition should be registered (e.g. "/sdcard")
|
||||
* @param host_config_input Pointer to structure describing SDMMC host. This structure can be
|
||||
* initialized using SDSPI_HOST_DEFAULT() macro.
|
||||
* @param slot_config Pointer to structure with slot configuration.
|
||||
* For SPI peripheral, pass a pointer to sdspi_device_config_t
|
||||
* structure initialized using SDSPI_DEVICE_CONFIG_DEFAULT().
|
||||
* @param mount_config pointer to structure with extra parameters for mounting FATFS
|
||||
* @param[out] out_card If not NULL, pointer to the card information structure will be returned via
|
||||
* this argument. It is suggested to hold this handle and use it to unmount the card later if
|
||||
* needed. Otherwise it's not suggested to use more than one card at the same time and unmount one
|
||||
* of them in your application.
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_STATE if esp_vfs_fat_sdmmc_mount was already called
|
||||
* - ESP_ERR_NO_MEM if memory can not be allocated
|
||||
* - ESP_FAIL if partition can not be mounted
|
||||
* - other error codes from SDMMC or SPI drivers, SDMMC protocol, or FATFS drivers
|
||||
*/
|
||||
esp_err_t esp_vfs_fat_sdspi_mount_mh(const char* base_path, const sdmmc_host_t* host_config_input, const sdspi_device_config_t* slot_config, const esp_vfs_fat_mount_config_t* mount_config, sdmmc_card_t** out_card);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#include "defines.h"
|
||||
|
||||
#include "statusled.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
@@ -5,7 +7,6 @@
|
||||
#include "driver/gpio.h"
|
||||
|
||||
#include "ClassLogFile.h"
|
||||
#include "../../include/defines.h"
|
||||
|
||||
// define `gpio_pad_select_gpip` for newer versions of IDF
|
||||
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0))
|
||||
@@ -13,28 +14,29 @@
|
||||
#define gpio_pad_select_gpio esp_rom_gpio_pad_select_gpio
|
||||
#endif
|
||||
|
||||
static const char* TAG = "STATUSLED";
|
||||
static const char *TAG = "STATUSLED";
|
||||
|
||||
TaskHandle_t xHandle_task_StatusLED = NULL;
|
||||
struct StatusLEDData StatusLEDData = {};
|
||||
|
||||
|
||||
void task_StatusLED(void *pvParameter)
|
||||
void task_status_led(void *pvParameter)
|
||||
{
|
||||
//ESP_LOGD(TAG, "task_StatusLED - create");
|
||||
// ESP_LOGD(TAG, "task_StatusLED - create");
|
||||
while (StatusLEDData.bProcessingRequest)
|
||||
{
|
||||
//ESP_LOGD(TAG, "task_StatusLED - start");
|
||||
// ESP_LOGD(TAG, "task_StatusLED - start");
|
||||
struct StatusLEDData StatusLEDDataInt = StatusLEDData;
|
||||
|
||||
gpio_pad_select_gpio(BLINK_GPIO); // Init the GPIO
|
||||
gpio_pad_select_gpio(BLINK_GPIO); // Init the GPIO
|
||||
gpio_set_direction(BLINK_GPIO, GPIO_MODE_OUTPUT); // Set the GPIO as a push/pull output
|
||||
gpio_set_level(BLINK_GPIO, 1);// LED off
|
||||
gpio_set_level(BLINK_GPIO, 1); // LED off
|
||||
|
||||
for (int i=0; i<2; ) // Default: repeat 2 times
|
||||
for (int i = 0; i < 2;) // Default: repeat 2 times
|
||||
{
|
||||
if (!StatusLEDDataInt.bInfinite)
|
||||
{
|
||||
++i;
|
||||
}
|
||||
|
||||
for (int j = 0; j < StatusLEDDataInt.iSourceBlinkCnt; ++j)
|
||||
{
|
||||
@@ -44,7 +46,7 @@ void task_StatusLED(void *pvParameter)
|
||||
vTaskDelay(StatusLEDDataInt.iBlinkTime / portTICK_PERIOD_MS);
|
||||
}
|
||||
|
||||
vTaskDelay(500 / portTICK_PERIOD_MS); // Delay between module code and error code
|
||||
vTaskDelay(500 / portTICK_PERIOD_MS); // Delay between module code and error code
|
||||
|
||||
for (int j = 0; j < StatusLEDDataInt.iCodeBlinkCnt; ++j)
|
||||
{
|
||||
@@ -53,101 +55,112 @@ void task_StatusLED(void *pvParameter)
|
||||
gpio_set_level(BLINK_GPIO, 1);
|
||||
vTaskDelay(StatusLEDDataInt.iBlinkTime / portTICK_PERIOD_MS);
|
||||
}
|
||||
vTaskDelay(1500 / portTICK_PERIOD_MS); // Delay to signal new round
|
||||
vTaskDelay(1500 / portTICK_PERIOD_MS); // Delay to signal new round
|
||||
}
|
||||
|
||||
StatusLEDData.bProcessingRequest = false;
|
||||
//ESP_LOGD(TAG, "task_StatusLED - done/wait");
|
||||
vTaskDelay(10000 / portTICK_PERIOD_MS); // Wait for an upcoming request otherwise continue and delete task to save memory
|
||||
// ESP_LOGD(TAG, "task_StatusLED - done/wait");
|
||||
vTaskDelay(10000 / portTICK_PERIOD_MS); // Wait for an upcoming request otherwise continue and delete task to save memory
|
||||
}
|
||||
//ESP_LOGD(TAG, "task_StatusLED - delete");
|
||||
// ESP_LOGD(TAG, "task_StatusLED - delete");
|
||||
xHandle_task_StatusLED = NULL;
|
||||
vTaskDelete(NULL); // Delete this task due to no request
|
||||
vTaskDelete(NULL); // Delete this task due to no request
|
||||
}
|
||||
|
||||
|
||||
void StatusLED(StatusLedSource _eSource, int _iCode, bool _bInfinite)
|
||||
void set_status_led(StatusLedSource _eSource, int _iCode, bool _bInfinite)
|
||||
{
|
||||
//ESP_LOGD(TAG, "StatusLED - start");
|
||||
// ESP_LOGD(TAG, "StatusLED - start");
|
||||
|
||||
if (_eSource == WLAN_CONN) {
|
||||
if (_eSource == WLAN_CONN)
|
||||
{
|
||||
StatusLEDData.iSourceBlinkCnt = WLAN_CONN;
|
||||
StatusLEDData.iCodeBlinkCnt = _iCode;
|
||||
StatusLEDData.iBlinkTime = 250;
|
||||
StatusLEDData.bInfinite = _bInfinite;
|
||||
}
|
||||
else if (_eSource == WLAN_INIT) {
|
||||
else if (_eSource == WLAN_INIT)
|
||||
{
|
||||
StatusLEDData.iSourceBlinkCnt = WLAN_INIT;
|
||||
StatusLEDData.iCodeBlinkCnt = _iCode;
|
||||
StatusLEDData.iBlinkTime = 250;
|
||||
StatusLEDData.bInfinite = _bInfinite;
|
||||
}
|
||||
else if (_eSource == SDCARD_INIT) {
|
||||
else if (_eSource == SDCARD_INIT)
|
||||
{
|
||||
StatusLEDData.iSourceBlinkCnt = SDCARD_INIT;
|
||||
StatusLEDData.iCodeBlinkCnt = _iCode;
|
||||
StatusLEDData.iBlinkTime = 250;
|
||||
StatusLEDData.bInfinite = _bInfinite;
|
||||
}
|
||||
else if (_eSource == SDCARD_CHECK) {
|
||||
else if (_eSource == SDCARD_CHECK)
|
||||
{
|
||||
StatusLEDData.iSourceBlinkCnt = SDCARD_CHECK;
|
||||
StatusLEDData.iCodeBlinkCnt = _iCode;
|
||||
StatusLEDData.iBlinkTime = 250;
|
||||
StatusLEDData.bInfinite = _bInfinite;
|
||||
}
|
||||
else if (_eSource == CAM_INIT) {
|
||||
else if (_eSource == CAM_INIT)
|
||||
{
|
||||
StatusLEDData.iSourceBlinkCnt = CAM_INIT;
|
||||
StatusLEDData.iCodeBlinkCnt = _iCode;
|
||||
StatusLEDData.iBlinkTime = 250;
|
||||
StatusLEDData.bInfinite = _bInfinite;
|
||||
}
|
||||
else if (_eSource == PSRAM_INIT) {
|
||||
else if (_eSource == PSRAM_INIT)
|
||||
{
|
||||
StatusLEDData.iSourceBlinkCnt = PSRAM_INIT;
|
||||
StatusLEDData.iCodeBlinkCnt = _iCode;
|
||||
StatusLEDData.iBlinkTime = 250;
|
||||
StatusLEDData.bInfinite = _bInfinite;
|
||||
}
|
||||
else if (_eSource == TIME_CHECK) {
|
||||
else if (_eSource == TIME_CHECK)
|
||||
{
|
||||
StatusLEDData.iSourceBlinkCnt = TIME_CHECK;
|
||||
StatusLEDData.iCodeBlinkCnt = _iCode;
|
||||
StatusLEDData.iBlinkTime = 250;
|
||||
StatusLEDData.bInfinite = _bInfinite;
|
||||
}
|
||||
else if (_eSource == AP_OR_OTA) {
|
||||
else if (_eSource == AP_OR_OTA)
|
||||
{
|
||||
StatusLEDData.iSourceBlinkCnt = AP_OR_OTA;
|
||||
StatusLEDData.iCodeBlinkCnt = _iCode;
|
||||
StatusLEDData.iBlinkTime = 350;
|
||||
StatusLEDData.bInfinite = _bInfinite;
|
||||
}
|
||||
|
||||
if (xHandle_task_StatusLED && !StatusLEDData.bProcessingRequest) {
|
||||
if (xHandle_task_StatusLED && !StatusLEDData.bProcessingRequest)
|
||||
{
|
||||
StatusLEDData.bProcessingRequest = true;
|
||||
BaseType_t xReturned = xTaskAbortDelay(xHandle_task_StatusLED); // Reuse still running status LED task
|
||||
/*if (xReturned == pdPASS)
|
||||
ESP_LOGD(TAG, "task_StatusLED - abort waiting delay");*/
|
||||
BaseType_t xReturned = xTaskAbortDelay(xHandle_task_StatusLED); // Reuse still running status LED task
|
||||
/*if (xReturned == pdPASS)
|
||||
ESP_LOGD(TAG, "task_StatusLED - abort waiting delay");*/
|
||||
}
|
||||
else if (xHandle_task_StatusLED == NULL) {
|
||||
else if (xHandle_task_StatusLED == NULL)
|
||||
{
|
||||
StatusLEDData.bProcessingRequest = true;
|
||||
BaseType_t xReturned = xTaskCreate(&task_StatusLED, "task_StatusLED", 1280, NULL, tskIDLE_PRIORITY+1, &xHandle_task_StatusLED);
|
||||
if(xReturned != pdPASS)
|
||||
BaseType_t xReturned = xTaskCreate(&task_status_led, "task_StatusLED", 1280, NULL, tskIDLE_PRIORITY + 1, &xHandle_task_StatusLED);
|
||||
if (xReturned != pdPASS)
|
||||
{
|
||||
xHandle_task_StatusLED = NULL;
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "task_StatusLED failed to create");
|
||||
LogFile.WriteHeapInfo("task_StatusLED failed");
|
||||
LogFile.WriteHeapInfo("task_StatusLED failed");
|
||||
}
|
||||
}
|
||||
else {
|
||||
ESP_LOGD(TAG, "task_StatusLED still processing, request skipped"); // Requests with high frequency could be skipped, but LED is only helpful for static states
|
||||
else
|
||||
{
|
||||
ESP_LOGD(TAG, "task_StatusLED still processing, request skipped"); // Requests with high frequency could be skipped, but LED is only helpful for static states
|
||||
}
|
||||
//ESP_LOGD(TAG, "StatusLED - done");
|
||||
// ESP_LOGD(TAG, "StatusLED - done");
|
||||
}
|
||||
|
||||
|
||||
void StatusLEDOff(void)
|
||||
void set_status_led_off(void)
|
||||
{
|
||||
if (xHandle_task_StatusLED)
|
||||
{
|
||||
vTaskDelete(xHandle_task_StatusLED); // Delete task for StatusLED to force stop of blinking
|
||||
}
|
||||
|
||||
gpio_pad_select_gpio(BLINK_GPIO); // Init the GPIO
|
||||
gpio_pad_select_gpio(BLINK_GPIO); // Init the GPIO
|
||||
gpio_set_direction(BLINK_GPIO, GPIO_MODE_OUTPUT); // Set the GPIO as a push/pull output
|
||||
gpio_set_level(BLINK_GPIO, 1);// LED off
|
||||
gpio_set_level(BLINK_GPIO, 1); // LED off
|
||||
}
|
||||
@@ -6,21 +6,22 @@
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
|
||||
extern TaskHandle_t xHandle_task_StatusLED;
|
||||
|
||||
enum StatusLedSource {
|
||||
WLAN_CONN = 1,
|
||||
enum StatusLedSource
|
||||
{
|
||||
WLAN_CONN = 1,
|
||||
WLAN_INIT = 2,
|
||||
SDCARD_INIT = 3,
|
||||
SDCARD_CHECK = 4,
|
||||
SDCARD_CHECK = 4,
|
||||
CAM_INIT = 5,
|
||||
PSRAM_INIT = 6,
|
||||
TIME_CHECK = 7,
|
||||
AP_OR_OTA = 8
|
||||
};
|
||||
|
||||
struct StatusLEDData {
|
||||
struct StatusLEDData
|
||||
{
|
||||
int iSourceBlinkCnt = 1;
|
||||
int iCodeBlinkCnt = 1;
|
||||
int iBlinkTime = 250;
|
||||
@@ -28,7 +29,7 @@ struct StatusLEDData {
|
||||
bool bProcessingRequest = false;
|
||||
};
|
||||
|
||||
void StatusLED(StatusLedSource _eSource, int _iCode, bool _bInfinite);
|
||||
void StatusLEDOff(void);
|
||||
void set_status_led(StatusLedSource _eSource, int _iCode, bool _bInfinite);
|
||||
void set_status_led_off(void);
|
||||
|
||||
#endif //STATUSLED_H
|
||||
#endif // STATUSLED_H
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
#include "defines.h"
|
||||
|
||||
#include "CAlignAndCutImage.h"
|
||||
#include "ClassFlowAlignment.h"
|
||||
#include "ClassControllCamera.h"
|
||||
#include "CRotateImage.h"
|
||||
#include "ClassLogFile.h"
|
||||
|
||||
@@ -6,9 +10,8 @@
|
||||
#include <algorithm>
|
||||
#include <esp_log.h>
|
||||
#include "psram.h"
|
||||
#include "../../include/defines.h"
|
||||
|
||||
static const char* TAG = "c_align_and_cut_image";
|
||||
static const char *TAG = "c_align_and_cut_image";
|
||||
|
||||
CAlignAndCutImage::CAlignAndCutImage(std::string _name, CImageBasis *_org, CImageBasis *_temp) : CImageBasis(_name)
|
||||
{
|
||||
@@ -19,9 +22,7 @@ CAlignAndCutImage::CAlignAndCutImage(std::string _name, CImageBasis *_org, CImag
|
||||
height = _org->height;
|
||||
bpp = _org->bpp;
|
||||
externalImage = true;
|
||||
|
||||
islocked = false;
|
||||
|
||||
ImageTMP = _temp;
|
||||
}
|
||||
|
||||
@@ -33,118 +34,122 @@ void CAlignAndCutImage::GetRefSize(int *ref_dx, int *ref_dy)
|
||||
ref_dy[1] = t1_dy;
|
||||
}
|
||||
|
||||
bool CAlignAndCutImage::Align(RefInfo *_temp1, RefInfo *_temp2)
|
||||
int CAlignAndCutImage::Align(RefInfo *_temp1, RefInfo *_temp2)
|
||||
{
|
||||
int dx, dy;
|
||||
int r0_x, r0_y, r1_x, r1_y;
|
||||
bool isSimilar1, isSimilar2;
|
||||
CFindTemplate *ft = new CFindTemplate("align", rgb_image, channels, width, height, bpp);
|
||||
|
||||
CFindTemplate* ft = new CFindTemplate("align", rgb_image, channels, width, height, bpp);
|
||||
//////////////////////////////////////////////
|
||||
bool isSimilar1 = ft->FindTemplate(_temp1); // search the alignment image 1
|
||||
|
||||
r0_x = _temp1->target_x;
|
||||
r0_y = _temp1->target_y;
|
||||
ESP_LOGD(TAG, "Before ft->FindTemplate(_temp1); %s", _temp1->image_file.c_str());
|
||||
isSimilar1 = ft->FindTemplate(_temp1);
|
||||
_temp1->width = ft->tpl_width;
|
||||
_temp1->height = ft->tpl_height;
|
||||
|
||||
r1_x = _temp2->target_x;
|
||||
r1_y = _temp2->target_y;
|
||||
ESP_LOGD(TAG, "Before ft->FindTemplate(_temp2); %s", _temp2->image_file.c_str());
|
||||
isSimilar2 = ft->FindTemplate(_temp2);
|
||||
int x1_relative_shift = (_temp1->target_x - _temp1->found_x);
|
||||
int y1_relative_shift = (_temp1->target_y - _temp1->found_y);
|
||||
|
||||
int x1_absolute_shift = _temp1->target_x + (_temp1->target_x - _temp1->found_x);
|
||||
int y1_absolute_shift = _temp1->target_y + (_temp1->target_y - _temp1->found_y);
|
||||
|
||||
//////////////////////////////////////////////
|
||||
bool isSimilar2 = ft->FindTemplate(_temp2); // search the alignment image 2
|
||||
|
||||
_temp2->width = ft->tpl_width;
|
||||
_temp2->height = ft->tpl_height;
|
||||
|
||||
int x2_relative_shift = (_temp2->target_x - _temp2->found_x);
|
||||
int y2_relative_shift = (_temp2->target_y - _temp2->found_y);
|
||||
|
||||
int x2_absolute_shift = _temp2->target_x + (_temp2->target_x - _temp2->found_x);
|
||||
int y2_absolute_shift = _temp2->target_y + (_temp2->target_y - _temp2->found_y);
|
||||
|
||||
delete ft;
|
||||
|
||||
int ret = Alignment_OK;
|
||||
|
||||
dx = _temp1->target_x - _temp1->found_x;
|
||||
dy = _temp1->target_y - _temp1->found_y;
|
||||
//////////////////////////////////////////////
|
||||
float radians_org = atan2((_temp2->found_y - _temp1->found_y), (_temp2->found_x - _temp1->found_x));
|
||||
float radians_cur = atan2((y2_absolute_shift - y1_absolute_shift), (x2_absolute_shift - x1_absolute_shift));
|
||||
float rotate_angle = (radians_cur - radians_org) * 180 / M_PI; // radians to degrees
|
||||
|
||||
r0_x += dx;
|
||||
r0_y += dy;
|
||||
//////////////////////////////////////////////
|
||||
if ((fabs(rotate_angle) > _temp1->search_max_angle) || (fabs(rotate_angle) > _temp2->search_max_angle))
|
||||
{
|
||||
ret = Rotation_Alignment_Failed;
|
||||
}
|
||||
|
||||
r1_x += dx;
|
||||
r1_y += dy;
|
||||
|
||||
float w_org, w_ist, d_winkel;
|
||||
|
||||
w_org = atan2(_temp2->found_y - _temp1->found_y, _temp2->found_x - _temp1->found_x);
|
||||
w_ist = atan2(r1_y - r0_y, r1_x - r0_x);
|
||||
|
||||
d_winkel = (w_ist - w_org) * 180 / M_PI;
|
||||
|
||||
/*#ifdef DEBUG_DETAIL_ON
|
||||
std::string zw = "\tdx:\t" + std::to_string(dx) + "\tdy:\t" + std::to_string(dy) + "\td_winkel:\t" + std::to_string(d_winkel);
|
||||
zw = zw + "\tt1_x_y:\t" + std::to_string(_temp1->found_x) + "\t" + std::to_string(_temp1->found_y);
|
||||
zw = zw + "\tpara1_found_min_avg_max_SAD:\t" + std::to_string(_temp1->fastalg_min) + "\t" + std::to_string(_temp1->fastalg_avg) + "\t" + std::to_string(_temp1->fastalg_max) + "\t"+ std::to_string(_temp1->fastalg_SAD);
|
||||
zw = zw + "\tt2_x_y:\t" + std::to_string(_temp2->found_x) + "\t" + std::to_string(_temp2->found_y);
|
||||
zw = zw + "\tpara2_found_min_avg_max:\t" + std::to_string(_temp2->fastalg_min) + "\t" + std::to_string(_temp2->fastalg_avg) + "\t" + std::to_string(_temp2->fastalg_max) + "\t"+ std::to_string(_temp2->fastalg_SAD);
|
||||
LogFile.WriteToDedicatedFile("/sdcard/alignment.txt", zw);
|
||||
#endif*/
|
||||
if ((abs(x1_relative_shift) >= _temp1->search_x) || (abs(y1_relative_shift) >= _temp1->search_y))
|
||||
{
|
||||
ret = Shift_Alignment_Failed;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////
|
||||
CRotateImage rt("Align", this, ImageTMP);
|
||||
rt.Translate(dx, dy);
|
||||
rt.Rotate(d_winkel, _temp1->target_x, _temp1->target_y);
|
||||
ESP_LOGD(TAG, "Alignment: dx %d - dy %d - rot %f", dx, dy, d_winkel);
|
||||
|
||||
return (isSimilar1 && isSimilar2);
|
||||
if (rotate_angle != 0)
|
||||
{
|
||||
rt.Translate(x1_relative_shift, y1_relative_shift);
|
||||
|
||||
if (Camera.ImageAntialiasing)
|
||||
{
|
||||
rt.RotateAntiAliasing(rotate_angle, _temp1->target_x, _temp1->target_y);
|
||||
}
|
||||
else
|
||||
{
|
||||
rt.Rotate(rotate_angle, _temp1->target_x, _temp1->target_y);
|
||||
}
|
||||
}
|
||||
else if (x1_relative_shift != 0 || y1_relative_shift != 0)
|
||||
{
|
||||
rt.Translate(x1_relative_shift, y1_relative_shift);
|
||||
}
|
||||
|
||||
return ((isSimilar1 && isSimilar2) ? Fast_Alignment_OK : ret);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void CAlignAndCutImage::CutAndSave(std::string _template1, int x1, int y1, int dx, int dy)
|
||||
{
|
||||
|
||||
int x2, y2;
|
||||
|
||||
x2 = x1 + dx;
|
||||
y2 = y1 + dy;
|
||||
x2 = std::min(x2, width - 1);
|
||||
y2 = std::min(y2, height - 1);
|
||||
int x2 = std::min((x1 + dx), (width - 1));
|
||||
int y2 = std::min((y1 + dy), (height - 1));
|
||||
|
||||
dx = x2 - x1;
|
||||
dy = y2 - y1;
|
||||
|
||||
int memsize = dx * dy * channels;
|
||||
uint8_t* odata = (unsigned char*) malloc_psram_heap(std::string(TAG) + "->odata", memsize, MALLOC_CAP_SPIRAM);
|
||||
uint8_t *temp_image = (unsigned char *)malloc_psram_heap(std::string(TAG) + "->temp_image", memsize, MALLOC_CAP_SPIRAM);
|
||||
|
||||
stbi_uc* p_target;
|
||||
stbi_uc* p_source;
|
||||
stbi_uc *p_target;
|
||||
stbi_uc *p_source;
|
||||
|
||||
RGBImageLock();
|
||||
|
||||
for (int x = x1; x < x2; ++x)
|
||||
{
|
||||
for (int y = y1; y < y2; ++y)
|
||||
{
|
||||
p_target = odata + (channels * ((y - y1) * dx + (x - x1)));
|
||||
p_target = temp_image + (channels * ((y - y1) * dx + (x - x1)));
|
||||
p_source = rgb_image + (channels * (y * width + x));
|
||||
|
||||
for (int _channels = 0; _channels < channels; ++_channels)
|
||||
{
|
||||
p_target[_channels] = p_source[_channels];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef STBI_ONLY_JPEG
|
||||
stbi_write_jpg(_template1.c_str(), dx, dy, channels, odata, 100);
|
||||
stbi_write_jpg(_template1.c_str(), dx, dy, channels, temp_image, 100);
|
||||
#else
|
||||
stbi_write_bmp(_template1.c_str(), dx, dy, channels, odata);
|
||||
stbi_write_bmp(_template1.c_str(), dx, dy, channels, temp_image);
|
||||
#endif
|
||||
|
||||
|
||||
RGBImageRelease();
|
||||
|
||||
stbi_image_free(odata);
|
||||
stbi_image_free(temp_image);
|
||||
}
|
||||
|
||||
void CAlignAndCutImage::CutAndSave(int x1, int y1, int dx, int dy, CImageBasis *_target)
|
||||
{
|
||||
int x2, y2;
|
||||
|
||||
x2 = x1 + dx;
|
||||
y2 = y1 + dy;
|
||||
x2 = std::min(x2, width - 1);
|
||||
y2 = std::min(y2, height - 1);
|
||||
int x2 = std::min((x1 + dx), (width - 1));
|
||||
int y2 = std::min((y1 + dy), (height - 1));
|
||||
|
||||
dx = x2 - x1;
|
||||
dy = y2 - y1;
|
||||
@@ -155,57 +160,63 @@ void CAlignAndCutImage::CutAndSave(int x1, int y1, int dx, int dy, CImageBasis *
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t* odata = _target->RGBImageLock();
|
||||
uint8_t *temp_image = _target->RGBImageLock();
|
||||
RGBImageLock();
|
||||
|
||||
stbi_uc* p_target;
|
||||
stbi_uc* p_source;
|
||||
stbi_uc *p_target;
|
||||
stbi_uc *p_source;
|
||||
|
||||
for (int x = x1; x < x2; ++x)
|
||||
{
|
||||
for (int y = y1; y < y2; ++y)
|
||||
{
|
||||
p_target = odata + (channels * ((y - y1) * dx + (x - x1)));
|
||||
p_target = temp_image + (channels * ((y - y1) * dx + (x - x1)));
|
||||
p_source = rgb_image + (channels * (y * width + x));
|
||||
|
||||
for (int _channels = 0; _channels < channels; ++_channels)
|
||||
{
|
||||
p_target[_channels] = p_source[_channels];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RGBImageRelease();
|
||||
_target->RGBImageRelease();
|
||||
}
|
||||
|
||||
|
||||
CImageBasis* CAlignAndCutImage::CutAndSave(int x1, int y1, int dx, int dy)
|
||||
CImageBasis *CAlignAndCutImage::CutAndSave(int x1, int y1, int dx, int dy)
|
||||
{
|
||||
int x2, y2;
|
||||
|
||||
x2 = x1 + dx;
|
||||
y2 = y1 + dy;
|
||||
x2 = std::min(x2, width - 1);
|
||||
y2 = std::min(y2, height - 1);
|
||||
int x2 = std::min((x1 + dx), (width - 1));
|
||||
int y2 = std::min((y1 + dy), (height - 1));
|
||||
|
||||
dx = x2 - x1;
|
||||
dy = y2 - y1;
|
||||
|
||||
int memsize = dx * dy * channels;
|
||||
uint8_t* odata = (unsigned char*)malloc_psram_heap(std::string(TAG) + "->odata", memsize, MALLOC_CAP_SPIRAM);
|
||||
uint8_t *temp_image = (unsigned char *)malloc_psram_heap(std::string(TAG) + "->temp_image", memsize, MALLOC_CAP_SPIRAM);
|
||||
|
||||
stbi_uc* p_target;
|
||||
stbi_uc* p_source;
|
||||
stbi_uc *p_target;
|
||||
stbi_uc *p_source;
|
||||
|
||||
RGBImageLock();
|
||||
|
||||
for (int x = x1; x < x2; ++x)
|
||||
{
|
||||
for (int y = y1; y < y2; ++y)
|
||||
{
|
||||
p_target = odata + (channels * ((y - y1) * dx + (x - x1)));
|
||||
p_target = temp_image + (channels * ((y - y1) * dx + (x - x1)));
|
||||
p_source = rgb_image + (channels * (y * width + x));
|
||||
for (int _channels = 0; _channels < channels; ++_channels)
|
||||
p_target[_channels] = p_source[_channels];
|
||||
}
|
||||
|
||||
CImageBasis* rs = new CImageBasis("CutAndSave", odata, channels, dx, dy, bpp);
|
||||
for (int _channels = 0; _channels < channels; ++_channels)
|
||||
{
|
||||
p_target[_channels] = p_source[_channels];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CImageBasis *rs = new CImageBasis("CutAndSave", temp_image, channels, dx, dy, bpp);
|
||||
RGBImageRelease();
|
||||
rs->SetIndepended();
|
||||
|
||||
return rs;
|
||||
}
|
||||
|
||||
@@ -6,22 +6,20 @@
|
||||
#include "CImageBasis.h"
|
||||
#include "CFindTemplate.h"
|
||||
|
||||
|
||||
class CAlignAndCutImage : public CImageBasis
|
||||
{
|
||||
public:
|
||||
public:
|
||||
int t0_dx, t0_dy, t1_dx, t1_dy;
|
||||
CImageBasis *ImageTMP;
|
||||
CAlignAndCutImage(std::string name, std::string _image) : CImageBasis(name, _image) {ImageTMP = NULL;};
|
||||
CAlignAndCutImage(std::string name, uint8_t* _rgb_image, int _channels, int _width, int _height, int _bpp) : CImageBasis(name, _rgb_image, _channels, _width, _height, _bpp) {ImageTMP = NULL;};
|
||||
CAlignAndCutImage(std::string name, std::string _image) : CImageBasis(name, _image) { ImageTMP = NULL; };
|
||||
CAlignAndCutImage(std::string name, uint8_t *_rgb_image, int _channels, int _width, int _height, int _bpp) : CImageBasis(name, _rgb_image, _channels, _width, _height, _bpp) { ImageTMP = NULL; };
|
||||
CAlignAndCutImage(std::string name, CImageBasis *_org, CImageBasis *_temp);
|
||||
|
||||
bool Align(RefInfo *_temp1, RefInfo *_temp2);
|
||||
// void Align(std::string _template1, int x1, int y1, std::string _template2, int x2, int y2, int deltax = 40, int deltay = 40, std::string imageROI = "");
|
||||
int Align(RefInfo *_temp1, RefInfo *_temp2);
|
||||
void CutAndSave(std::string _template1, int x1, int y1, int dx, int dy);
|
||||
CImageBasis* CutAndSave(int x1, int y1, int dx, int dy);
|
||||
CImageBasis *CutAndSave(int x1, int y1, int dx, int dy);
|
||||
void CutAndSave(int x1, int y1, int dx, int dy, CImageBasis *_target);
|
||||
void GetRefSize(int *ref_dx, int *ref_dy);
|
||||
};
|
||||
|
||||
#endif //CALIGNANDCUTIMAGE_H
|
||||
#endif // CALIGNANDCUTIMAGE_H
|
||||
|
||||
@@ -1,37 +1,30 @@
|
||||
#include "defines.h"
|
||||
|
||||
#include "CFindTemplate.h"
|
||||
|
||||
#include "ClassLogFile.h"
|
||||
#include "Helper.h"
|
||||
#include "../../include/defines.h"
|
||||
|
||||
#include <esp_log.h>
|
||||
|
||||
static const char* TAG = "C FIND TEMPL";
|
||||
|
||||
// #define DEBUG_DETAIL_ON
|
||||
|
||||
static const char *TAG = "C FIND TEMPL";
|
||||
|
||||
bool CFindTemplate::FindTemplate(RefInfo *_ref)
|
||||
{
|
||||
uint8_t* rgb_template;
|
||||
|
||||
if (file_size(_ref->image_file.c_str()) == 0) {
|
||||
if (file_size(_ref->image_file.c_str()) == 0)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, _ref->image_file + " is empty!");
|
||||
return false;
|
||||
}
|
||||
|
||||
rgb_template = stbi_load(_ref->image_file.c_str(), &tpl_width, &tpl_height, &tpl_bpp, channels);
|
||||
|
||||
if (rgb_template == NULL) {
|
||||
uint8_t *rgb_template = stbi_load(_ref->image_file.c_str(), &tpl_width, &tpl_height, &tpl_bpp, channels);
|
||||
if (rgb_template == NULL)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to load " + _ref->image_file + "! Is it corrupted?");
|
||||
return false;
|
||||
}
|
||||
|
||||
// ESP_LOGD(TAG, "FindTemplate 01");
|
||||
|
||||
int ow, ow_start, ow_stop;
|
||||
int oh, oh_start, oh_stop;
|
||||
|
||||
// ESP_LOGD(TAG, "FindTemplate 01");
|
||||
if (_ref->search_x == 0)
|
||||
{
|
||||
_ref->search_x = width;
|
||||
@@ -44,45 +37,39 @@ bool CFindTemplate::FindTemplate(RefInfo *_ref)
|
||||
_ref->found_y = 0;
|
||||
}
|
||||
|
||||
int x_search_area_start = std::max((_ref->target_x - _ref->search_x), 0);
|
||||
int x_search_area_stop = _ref->target_x + _ref->search_x;
|
||||
|
||||
ow_start = _ref->target_x - _ref->search_x;
|
||||
ow_start = std::max(ow_start, 0);
|
||||
ow_stop = _ref->target_x + _ref->search_x;
|
||||
if ((ow_stop + tpl_width) > width)
|
||||
ow_stop = width - tpl_width;
|
||||
ow = ow_stop - ow_start + 1;
|
||||
|
||||
oh_start = _ref->target_y - _ref->search_y;
|
||||
oh_start = std::max(oh_start, 0);
|
||||
oh_stop = _ref->target_y + _ref->search_y;
|
||||
if ((oh_stop + tpl_height) > height)
|
||||
oh_stop = height - tpl_height;
|
||||
oh = oh_stop - oh_start + 1;
|
||||
|
||||
float avg, SAD;
|
||||
int min, max;
|
||||
bool isSimilar = false;
|
||||
|
||||
// ESP_LOGD(TAG, "FindTemplate 02");
|
||||
|
||||
if ((_ref->alignment_algo == 2) && (_ref->fastalg_x > -1) && (_ref->fastalg_y > -1)) // für Testzwecke immer Berechnen
|
||||
if ((x_search_area_stop + tpl_width) > width)
|
||||
{
|
||||
isSimilar = CalculateSimularities(rgb_template, _ref->fastalg_x, _ref->fastalg_y, ow, oh, min, avg, max, SAD, _ref->fastalg_SAD, _ref->fastalg_SAD_criteria);
|
||||
/*#ifdef DEBUG_DETAIL_ON
|
||||
std::string zw = "\t" + _ref->image_file + "\tt1_x_y:\t" + std::to_string(_ref->fastalg_x) + "\t" + std::to_string(_ref->fastalg_y);
|
||||
zw = zw + "\tpara1_found_min_avg_max_SAD:\t" + std::to_string(min) + "\t" + std::to_string(avg) + "\t" + std::to_string(max) + "\t"+ std::to_string(SAD);
|
||||
LogFile.WriteToDedicatedFile("/sdcard/alignment.txt", zw);
|
||||
#endif*/
|
||||
x_search_area_stop = width - tpl_width;
|
||||
}
|
||||
|
||||
// ESP_LOGD(TAG, "FindTemplate 03");
|
||||
int x_search_area = x_search_area_stop - x_search_area_start + 1;
|
||||
|
||||
int y_search_area_start = std::max((_ref->target_y - _ref->search_y), 0);
|
||||
int y_search_area_stop = _ref->target_y + _ref->search_y;
|
||||
|
||||
if ((y_search_area_stop + tpl_height) > height)
|
||||
{
|
||||
y_search_area_stop = height - tpl_height;
|
||||
}
|
||||
|
||||
int y_search_area = y_search_area_stop - y_search_area_start + 1;
|
||||
|
||||
float avg = 0, SAD = 0;
|
||||
int min = 0, max = 0;
|
||||
bool isSimilar = false;
|
||||
|
||||
// ESP_LOGD(TAG, "FindTemplate 02");
|
||||
if ((_ref->alignment_algo == 2) && (_ref->fastalg_x > -1) && (_ref->fastalg_y > -1))
|
||||
{
|
||||
isSimilar = CalculateSimularities(rgb_template, _ref->fastalg_x, _ref->fastalg_y, x_search_area, y_search_area, min, avg, max, SAD, _ref->fastalg_SAD, _ref->fastalg_SAD_criteria);
|
||||
}
|
||||
|
||||
// ESP_LOGD(TAG, "FindTemplate 03");
|
||||
if (isSimilar)
|
||||
{
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Use FastAlignment sucessfull");
|
||||
#endif
|
||||
_ref->found_x = _ref->fastalg_x;
|
||||
_ref->found_y = _ref->fastalg_y;
|
||||
|
||||
@@ -91,101 +78,117 @@ bool CFindTemplate::FindTemplate(RefInfo *_ref)
|
||||
return true;
|
||||
}
|
||||
|
||||
// ESP_LOGD(TAG, "FindTemplate 04");
|
||||
|
||||
|
||||
double aktSAD;
|
||||
// ESP_LOGD(TAG, "FindTemplate 04");
|
||||
double minSAD = pow(tpl_width * tpl_height * 255, 2);
|
||||
|
||||
RGBImageLock();
|
||||
|
||||
// ESP_LOGD(TAG, "FindTemplate 05");
|
||||
int xouter, youter, tpl_x, tpl_y, _ch;
|
||||
// ESP_LOGD(TAG, "FindTemplate 05");
|
||||
int _anzchannels = channels;
|
||||
if (_ref->alignment_algo == 0) // 0 = "Default" (nur R-Kanal)
|
||||
_anzchannels = 1;
|
||||
|
||||
for (xouter = ow_start; xouter <= ow_stop; xouter++)
|
||||
for (youter = oh_start; youter <= oh_stop; ++youter)
|
||||
if (_ref->alignment_algo == 0)
|
||||
{
|
||||
// 0 = "Default" (nur R-Kanal)
|
||||
_anzchannels = 1;
|
||||
}
|
||||
|
||||
for (int x_outer = x_search_area_start; x_outer <= x_search_area_stop; x_outer++)
|
||||
{
|
||||
for (int y_outer = y_search_area_start; y_outer <= y_search_area_stop; ++y_outer)
|
||||
{
|
||||
aktSAD = 0;
|
||||
for (tpl_x = 0; tpl_x < tpl_width; tpl_x++)
|
||||
for (tpl_y = 0; tpl_y < tpl_height; tpl_y++)
|
||||
double aktSAD = 0;
|
||||
|
||||
for (int tpl_x = 0; tpl_x < tpl_width; tpl_x++)
|
||||
{
|
||||
for (int tpl_y = 0; tpl_y < tpl_height; tpl_y++)
|
||||
{
|
||||
stbi_uc* p_org = rgb_image + (channels * ((youter + tpl_y) * width + (xouter + tpl_x)));
|
||||
stbi_uc* p_tpl = rgb_template + (channels * (tpl_y * tpl_width + tpl_x));
|
||||
for (_ch = 0; _ch < _anzchannels; ++_ch)
|
||||
stbi_uc *p_org = rgb_image + (channels * ((y_outer + tpl_y) * width + (x_outer + tpl_x)));
|
||||
stbi_uc *p_tpl = rgb_template + (channels * (tpl_y * tpl_width + tpl_x));
|
||||
|
||||
for (int tpl_ch = 0; tpl_ch < _anzchannels; ++tpl_ch)
|
||||
{
|
||||
aktSAD += pow(p_tpl[_ch] - p_org[_ch], 2);
|
||||
aktSAD += pow(p_tpl[tpl_ch] - p_org[tpl_ch], 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (aktSAD < minSAD)
|
||||
{
|
||||
minSAD = aktSAD;
|
||||
_ref->found_x = xouter;
|
||||
_ref->found_y = youter;
|
||||
|
||||
_ref->found_x = x_outer;
|
||||
_ref->found_y = y_outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ESP_LOGD(TAG, "FindTemplate 06");
|
||||
|
||||
|
||||
// ESP_LOGD(TAG, "FindTemplate 06");
|
||||
if (_ref->alignment_algo == 2)
|
||||
CalculateSimularities(rgb_template, _ref->found_x, _ref->found_y, ow, oh, min, avg, max, SAD, _ref->fastalg_SAD, _ref->fastalg_SAD_criteria);
|
||||
{
|
||||
isSimilar = CalculateSimularities(rgb_template, _ref->found_x, _ref->found_y, x_search_area, y_search_area, min, avg, max, SAD, _ref->fastalg_SAD, _ref->fastalg_SAD_criteria);
|
||||
}
|
||||
|
||||
// ESP_LOGD(TAG, "FindTemplate 07");
|
||||
if (isSimilar)
|
||||
{
|
||||
_ref->fastalg_x = _ref->found_x;
|
||||
_ref->fastalg_y = _ref->found_y;
|
||||
|
||||
// ESP_LOGD(TAG, "FindTemplate 07");
|
||||
_ref->fastalg_min = min;
|
||||
_ref->fastalg_max = max;
|
||||
|
||||
_ref->fastalg_x = _ref->found_x;
|
||||
_ref->fastalg_y = _ref->found_y;
|
||||
_ref->fastalg_min = min;
|
||||
_ref->fastalg_avg = avg;
|
||||
_ref->fastalg_max = max;
|
||||
_ref->fastalg_SAD = SAD;
|
||||
_ref->fastalg_avg = avg;
|
||||
_ref->fastalg_SAD = SAD;
|
||||
|
||||
RGBImageRelease();
|
||||
stbi_image_free(rgb_template);
|
||||
|
||||
/*#ifdef DEBUG_DETAIL_ON
|
||||
std::string zw = "\t" + _ref->image_file + "\tt1_x_y:\t" + std::to_string(_ref->fastalg_x) + "\t" + std::to_string(_ref->fastalg_y);
|
||||
zw = zw + "\tpara1_found_min_avg_max_SAD:\t" + std::to_string(min) + "\t" + std::to_string(avg) + "\t" + std::to_string(max) + "\t"+ std::to_string(SAD);
|
||||
LogFile.WriteToDedicatedFile("/sdcard/alignment.txt", zw);
|
||||
#endif*/
|
||||
return true;
|
||||
}
|
||||
|
||||
RGBImageRelease();
|
||||
stbi_image_free(rgb_template);
|
||||
|
||||
// ESP_LOGD(TAG, "FindTemplate 08");
|
||||
|
||||
// ESP_LOGD(TAG, "FindTemplate 09");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool CFindTemplate::CalculateSimularities(uint8_t* _rgb_tmpl, int _startx, int _starty, int _sizex, int _sizey, int &min, float &avg, int &max, float &SAD, float _SADold, float _SADcrit)
|
||||
bool CFindTemplate::CalculateSimularities(uint8_t *_rgb_tmpl, int _startx, int _starty, int _sizex, int _sizey, int &min, float &avg, int &max, float &SAD, float _SADold, float _SADcrit)
|
||||
{
|
||||
int dif;
|
||||
int dif = 0;
|
||||
int minDif = 255;
|
||||
int maxDif = -255;
|
||||
double avgDifSum = 0;
|
||||
long int anz = 0;
|
||||
double aktSAD = 0;
|
||||
|
||||
int xouter, youter, _ch;
|
||||
|
||||
for (xouter = 0; xouter <= _sizex; xouter++)
|
||||
for (youter = 0; youter <= _sizey; ++youter)
|
||||
for (int x_outer = 0; x_outer <= _sizex; x_outer++)
|
||||
{
|
||||
for (int y_outer = 0; y_outer <= _sizey; ++y_outer)
|
||||
{
|
||||
stbi_uc* p_org = rgb_image + (channels * ((youter + _starty) * width + (xouter + _startx)));
|
||||
stbi_uc* p_tpl = _rgb_tmpl + (channels * (youter * tpl_width + xouter));
|
||||
for (_ch = 0; _ch < channels; ++_ch)
|
||||
stbi_uc *p_org = rgb_image + (channels * ((y_outer + _starty) * width + (x_outer + _startx)));
|
||||
stbi_uc *p_tpl = _rgb_tmpl + (channels * (y_outer * tpl_width + x_outer));
|
||||
|
||||
for (int _ch = 0; _ch < channels; ++_ch)
|
||||
{
|
||||
dif = p_tpl[_ch] - p_org[_ch];
|
||||
aktSAD += pow(p_tpl[_ch] - p_org[_ch], 2);
|
||||
if (dif < minDif) minDif = dif;
|
||||
if (dif > maxDif) maxDif = dif;
|
||||
|
||||
if (dif < minDif)
|
||||
{
|
||||
minDif = dif;
|
||||
}
|
||||
|
||||
if (dif > maxDif)
|
||||
{
|
||||
maxDif = dif;
|
||||
}
|
||||
|
||||
avgDifSum += dif;
|
||||
anz++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
avg = avgDifSum / anz;
|
||||
min = minDif;
|
||||
@@ -197,10 +200,9 @@ bool CFindTemplate::CalculateSimularities(uint8_t* _rgb_tmpl, int _startx, int _
|
||||
ESP_LOGD(TAG, "Anzahl %ld, avgDifSum %fd, avg %f, SAD_neu: %fd, _SAD_old: %f, _SAD_crit:%f", anz, avgDifSum, avg, SAD, _SADold, _SADdif);
|
||||
|
||||
if (_SADdif <= _SADcrit)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -5,16 +5,18 @@
|
||||
|
||||
#include "CImageBasis.h"
|
||||
|
||||
struct RefInfo {
|
||||
struct RefInfo
|
||||
{
|
||||
std::string image_file;
|
||||
int target_x = 0;
|
||||
int target_y = 0;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
int target_x = 0; // X-coordinate of the alignment image
|
||||
int target_y = 0; // Y-coordinate of the alignment image
|
||||
int width = 0; // Width of the alignment image
|
||||
int height = 0; // Height of the alignment image
|
||||
int search_x; // X-size (width) in which the reference is searched
|
||||
int search_y; // Y-size (height) in which the reference is searched
|
||||
float search_max_angle; // Max rotation angle in which the reference is searched
|
||||
int found_x;
|
||||
int found_y;
|
||||
int search_x;
|
||||
int search_y;
|
||||
int fastalg_x = -1;
|
||||
int fastalg_y = -1;
|
||||
int fastalg_min = -256;
|
||||
@@ -22,21 +24,23 @@ struct RefInfo {
|
||||
int fastalg_max = -1;
|
||||
float fastalg_SAD = -1;
|
||||
float fastalg_SAD_criteria = -1;
|
||||
int alignment_algo = 0; // 0 = "Default" (nur R-Kanal), 1 = "HighAccuracy" (RGB-Kanal), 2 = "Fast" (1.x RGB, dann isSimilar)
|
||||
int alignment_algo = 0; // 0 = "Default" (nur R-Kanal), 1 = "HighAccuracy" (RGB-Kanal), 2 = "Fast" (1.x RGB, dann isSimilar)
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
class CFindTemplate : public CImageBasis
|
||||
{
|
||||
public:
|
||||
int tpl_width, tpl_height, tpl_bpp;
|
||||
CFindTemplate(std::string name, uint8_t* _rgb_image, int _channels, int _width, int _height, int _bpp) : CImageBasis(name, _rgb_image, _channels, _width, _height, _bpp) {};
|
||||
public:
|
||||
int tpl_width, tpl_height, tpl_bpp;
|
||||
|
||||
bool FindTemplate(RefInfo *_ref);
|
||||
#define Fast_Alignment_OK 1
|
||||
#define Alignment_OK 0
|
||||
#define Alignment_Failed -1
|
||||
#define Rotation_Alignment_Failed -2
|
||||
#define Shift_Alignment_Failed -3
|
||||
|
||||
bool CalculateSimularities(uint8_t* _rgb_tmpl, int _startx, int _starty, int _sizex, int _sizey, int &min, float &avg, int &max, float &SAD, float _SADold, float _SADcrit);
|
||||
CFindTemplate(std::string name, uint8_t *_rgb_image, int _channels, int _width, int _height, int _bpp) : CImageBasis(name, _rgb_image, _channels, _width, _height, _bpp) {};
|
||||
bool FindTemplate(RefInfo *_ref);
|
||||
bool CalculateSimularities(uint8_t *_rgb_tmpl, int _startx, int _starty, int _sizex, int _sizey, int &min, float &avg, int &max, float &SAD, float _SADold, float _SADcrit);
|
||||
};
|
||||
|
||||
#endif //CFINDTEMPLATE_H
|
||||
#endif // CFINDTEMPLATE_H
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#include "defines.h"
|
||||
|
||||
#include "CImageBasis.h"
|
||||
#include "Helper.h"
|
||||
#include "psram.h"
|
||||
@@ -5,116 +7,111 @@
|
||||
#include "server_ota.h"
|
||||
|
||||
#include <esp_log.h>
|
||||
#include "../../include/defines.h"
|
||||
|
||||
#include "esp_system.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include <math.h>
|
||||
#include <algorithm>
|
||||
|
||||
|
||||
using namespace std;
|
||||
|
||||
static const char *TAG = "C IMG BASIS";
|
||||
|
||||
bool jpgFileTooLarge = false; // JPG creation verfication
|
||||
bool jpgFileTooLarge = false; // JPG creation verfication
|
||||
|
||||
|
||||
//#define DEBUG_DETAIL_ON
|
||||
|
||||
|
||||
uint8_t * CImageBasis::RGBImageLock(int _waitmaxsec)
|
||||
uint8_t *CImageBasis::RGBImageLock(int _waitmaxsec)
|
||||
{
|
||||
if (islocked)
|
||||
{
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
ESP_LOGD(TAG, "Image is locked: sleep for: %ds", _waitmaxsec);
|
||||
#endif
|
||||
TickType_t xDelay;
|
||||
xDelay = 1000 / portTICK_PERIOD_MS;
|
||||
for (int i = 0; i <= _waitmaxsec; ++i)
|
||||
{
|
||||
vTaskDelay( xDelay );
|
||||
vTaskDelay(xDelay);
|
||||
if (!islocked)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (islocked)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return rgb_image;
|
||||
}
|
||||
|
||||
|
||||
void CImageBasis::RGBImageRelease()
|
||||
{
|
||||
islocked = false;
|
||||
}
|
||||
|
||||
|
||||
uint8_t * CImageBasis::RGBImageGet()
|
||||
uint8_t *CImageBasis::RGBImageGet()
|
||||
{
|
||||
return rgb_image;
|
||||
}
|
||||
|
||||
|
||||
void writejpghelp(void *context, void *data, int size)
|
||||
{
|
||||
// ESP_LOGD(TAG, "Size all: %d, size %d", ((ImageData*)context)->size, size);
|
||||
ImageData* _zw = (ImageData*) context;
|
||||
// ESP_LOGD(TAG, "Size all: %d, size %d", ((ImageData*)context)->size, size);
|
||||
ImageData *_zw = (ImageData *)context;
|
||||
uint8_t *voidstart = _zw->data;
|
||||
uint8_t *datastart = (uint8_t*) data;
|
||||
uint8_t *datastart = (uint8_t *)data;
|
||||
|
||||
if ((_zw->size < MAX_JPG_SIZE)) { // Abort copy to prevent buffer overflow
|
||||
if ((_zw->size < MAX_JPG_SIZE))
|
||||
{
|
||||
// Abort copy to prevent buffer overflow
|
||||
voidstart += _zw->size;
|
||||
|
||||
for (int i = 0; i < size; ++i)
|
||||
{
|
||||
*(voidstart + i) = *(datastart + i);
|
||||
}
|
||||
|
||||
_zw->size += size;
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
jpgFileTooLarge = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ImageData* CImageBasis::writeToMemoryAsJPG(const int quality)
|
||||
ImageData *CImageBasis::writeToMemoryAsJPG(const int quality)
|
||||
{
|
||||
ImageData* ii = new ImageData;
|
||||
ImageData *ii = new ImageData;
|
||||
|
||||
RGBImageLock();
|
||||
stbi_write_jpg_to_func(writejpghelp, ii, width, height, channels, rgb_image, quality);
|
||||
RGBImageRelease();
|
||||
|
||||
if (jpgFileTooLarge) {
|
||||
if (jpgFileTooLarge)
|
||||
{
|
||||
jpgFileTooLarge = false;
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "writeToMemoryAsJPG: Creation aborted! JPG size > preallocated buffer: " + std::to_string(MAX_JPG_SIZE));
|
||||
}
|
||||
return ii;
|
||||
}
|
||||
|
||||
|
||||
void CImageBasis::writeToMemoryAsJPG(ImageData* i, const int quality)
|
||||
void CImageBasis::writeToMemoryAsJPG(ImageData *i, const int quality)
|
||||
{
|
||||
ImageData* ii = new ImageData;
|
||||
ImageData *ii = new ImageData;
|
||||
|
||||
RGBImageLock();
|
||||
stbi_write_jpg_to_func(writejpghelp, ii, width, height, channels, rgb_image, quality);
|
||||
RGBImageRelease();
|
||||
|
||||
if (jpgFileTooLarge) {
|
||||
if (jpgFileTooLarge)
|
||||
{
|
||||
jpgFileTooLarge = false;
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "writeToMemoryAsJPG: Creation aborted! JPG size > preallocated buffer: " + std::to_string(MAX_JPG_SIZE));
|
||||
}
|
||||
memCopy((uint8_t*) ii, (uint8_t*) i, sizeof(ImageData));
|
||||
memCopy((uint8_t *)ii, (uint8_t *)i, sizeof(ImageData));
|
||||
delete ii;
|
||||
}
|
||||
|
||||
|
||||
struct SendJPGHTTP
|
||||
{
|
||||
httpd_req_t *req;
|
||||
@@ -123,24 +120,24 @@ struct SendJPGHTTP
|
||||
int size = 0;
|
||||
};
|
||||
|
||||
|
||||
inline void writejpgtohttphelp(void *context, void *data, int size)
|
||||
{
|
||||
SendJPGHTTP* _send = (SendJPGHTTP*) context;
|
||||
if ((_send->size + size) >= HTTP_BUFFER_SENT) // data no longer fits in buffer
|
||||
SendJPGHTTP *_send = (SendJPGHTTP *)context;
|
||||
|
||||
// data no longer fits in buffer
|
||||
if ((_send->size + size) >= HTTP_BUFFER_SENT)
|
||||
{
|
||||
if (httpd_resp_send_chunk(_send->req, _send->buf, _send->size) != ESP_OK)
|
||||
{
|
||||
ESP_LOGE(TAG, "File sending failed!");
|
||||
_send->res = ESP_FAIL;
|
||||
ESP_LOGE(TAG, "File sending failed!");
|
||||
_send->res = ESP_FAIL;
|
||||
}
|
||||
_send->size = 0;
|
||||
}
|
||||
std::memcpy((void*) (&(_send->buf[0]) + _send->size), data, size);
|
||||
_send->size+= size;
|
||||
std::memcpy((void *)(&(_send->buf[0]) + _send->size), data, size);
|
||||
_send->size += size;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t CImageBasis::SendJPGtoHTTP(httpd_req_t *_req, const int quality)
|
||||
{
|
||||
SendJPGHTTP ii;
|
||||
@@ -153,7 +150,8 @@ esp_err_t CImageBasis::SendJPGtoHTTP(httpd_req_t *_req, const int quality)
|
||||
|
||||
if (ii.size > 0)
|
||||
{
|
||||
if (httpd_resp_send_chunk(_req, (char*) ii.buf, ii.size) != ESP_OK) //still send the rest
|
||||
// still send the rest
|
||||
if (httpd_resp_send_chunk(_req, (char *)ii.buf, ii.size) != ESP_OK)
|
||||
{
|
||||
ESP_LOGE(TAG, "File sending failed!");
|
||||
ii.res = ESP_FAIL;
|
||||
@@ -165,11 +163,11 @@ esp_err_t CImageBasis::SendJPGtoHTTP(httpd_req_t *_req, const int quality)
|
||||
return ii.res;
|
||||
}
|
||||
|
||||
|
||||
bool CImageBasis::CopyFromMemory(uint8_t* _source, int _size)
|
||||
bool CImageBasis::CopyFromMemory(uint8_t *_source, int _size)
|
||||
{
|
||||
int gr = height * width * channels;
|
||||
if (gr != _size) // Size does not fit
|
||||
// Size does not fit
|
||||
if (gr != _size)
|
||||
{
|
||||
ESP_LOGE(TAG, "Cannot copy image from memory - sizes do not match: should be %d, but is %d", _size, gr);
|
||||
return false;
|
||||
@@ -182,46 +180,48 @@ bool CImageBasis::CopyFromMemory(uint8_t* _source, int _size)
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
uint8_t CImageBasis::GetPixelColor(int x, int y, int ch)
|
||||
{
|
||||
stbi_uc* p_source;
|
||||
stbi_uc *p_source;
|
||||
p_source = rgb_image + (channels * (y * width + x));
|
||||
return p_source[ch];
|
||||
}
|
||||
|
||||
|
||||
void CImageBasis::memCopy(uint8_t* _source, uint8_t* _target, int _size)
|
||||
void CImageBasis::memCopy(uint8_t *_source, uint8_t *_target, int _size)
|
||||
{
|
||||
#ifdef _ESP32_PSRAM
|
||||
#if CONFIG_SPIRAM
|
||||
for (int i = 0; i < _size; ++i)
|
||||
{
|
||||
*(_target + i) = *(_source + i);
|
||||
}
|
||||
#else
|
||||
memcpy(_target, _source, _size);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
bool CImageBasis::isInImage(int x, int y)
|
||||
{
|
||||
if ((x < 0) || (x > width - 1))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((y < 0) || (y > height- 1))
|
||||
if ((y < 0) || (y > height - 1))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void CImageBasis::setPixelColor(int x, int y, int r, int g, int b)
|
||||
{
|
||||
stbi_uc* p_source;
|
||||
stbi_uc *p_source;
|
||||
|
||||
RGBImageLock();
|
||||
p_source = rgb_image + (channels * (y * width + x));
|
||||
p_source[0] = r;
|
||||
if ( channels > 2)
|
||||
if (channels > 2)
|
||||
{
|
||||
p_source[1] = g;
|
||||
p_source[2] = b;
|
||||
@@ -229,7 +229,6 @@ void CImageBasis::setPixelColor(int x, int y, int r, int g, int b)
|
||||
RGBImageRelease();
|
||||
}
|
||||
|
||||
|
||||
void CImageBasis::drawRect(int x, int y, int dx, int dy, int r, int g, int b, int thickness)
|
||||
{
|
||||
int zwx1, zwx2, zwy1, zwy2;
|
||||
@@ -243,54 +242,86 @@ void CImageBasis::drawRect(int x, int y, int dx, int dy, int r, int g, int b, in
|
||||
RGBImageLock();
|
||||
|
||||
for (_thick = 0; _thick < thickness; _thick++)
|
||||
{
|
||||
for (_x = zwx1; _x <= zwx2; ++_x)
|
||||
{
|
||||
for (_y = zwy1; _y <= zwy2; _y++)
|
||||
{
|
||||
if (isInImage(_x, _y))
|
||||
{
|
||||
setPixelColor(_x, _y - _thick, r, g, b);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
zwx1 = x - thickness + 1;
|
||||
zwx2 = x + dx + thickness - 1;
|
||||
zwy1 = y + dy;
|
||||
zwy2 = y + dy;
|
||||
for (_thick = 0; _thick < thickness; _thick++)
|
||||
{
|
||||
for (_x = zwx1; _x <= zwx2; ++_x)
|
||||
{
|
||||
for (_y = zwy1; _y <= zwy2; _y++)
|
||||
{
|
||||
if (isInImage(_x, _y))
|
||||
{
|
||||
setPixelColor(_x, _y + _thick, r, g, b);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
zwx1 = x;
|
||||
zwx2 = x;
|
||||
zwy1 = y;
|
||||
zwy2 = y + dy;
|
||||
for (_thick = 0; _thick < thickness; _thick++)
|
||||
{
|
||||
for (_x = zwx1; _x <= zwx2; ++_x)
|
||||
{
|
||||
for (_y = zwy1; _y <= zwy2; _y++)
|
||||
{
|
||||
if (isInImage(_x, _y))
|
||||
{
|
||||
setPixelColor(_x - _thick, _y, r, g, b);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
zwx1 = x + dx;
|
||||
zwx2 = x + dx;
|
||||
zwy1 = y;
|
||||
zwy2 = y + dy;
|
||||
for (_thick = 0; _thick < thickness; _thick++)
|
||||
{
|
||||
for (_x = zwx1; _x <= zwx2; ++_x)
|
||||
{
|
||||
for (_y = zwy1; _y <= zwy2; _y++)
|
||||
{
|
||||
if (isInImage(_x, _y))
|
||||
{
|
||||
setPixelColor(_x + _thick, _y, r, g, b);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RGBImageRelease();
|
||||
}
|
||||
|
||||
|
||||
void CImageBasis::drawLine(int x1, int y1, int x2, int y2, int r, int g, int b, int thickness)
|
||||
{
|
||||
int _x, _y, _thick;
|
||||
int _zwy1, _zwy2;
|
||||
thickness = (thickness-1) / 2;
|
||||
thickness = (thickness - 1) / 2;
|
||||
|
||||
RGBImageLock();
|
||||
|
||||
for (_thick = 0; _thick <= thickness; ++_thick)
|
||||
{
|
||||
for (_x = x1 - _thick; _x <= x2 + _thick; ++_x)
|
||||
{
|
||||
if (x2 == x1)
|
||||
@@ -305,14 +336,18 @@ void CImageBasis::drawLine(int x1, int y1, int x2, int y2, int r, int g, int b,
|
||||
}
|
||||
|
||||
for (_y = _zwy1 - _thick; _y <= _zwy2 + _thick; _y++)
|
||||
{
|
||||
if (isInImage(_x, _y))
|
||||
{
|
||||
setPixelColor(_x, _y, r, g, b);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RGBImageRelease();
|
||||
}
|
||||
|
||||
|
||||
void CImageBasis::drawEllipse(int x1, int y1, int radx, int rady, int r, int g, int b, int thickness)
|
||||
{
|
||||
float deltarad, aktrad;
|
||||
@@ -320,25 +355,30 @@ void CImageBasis::drawEllipse(int x1, int y1, int radx, int rady, int r, int g,
|
||||
int rad = radx;
|
||||
|
||||
if (rady > radx)
|
||||
{
|
||||
rad = rady;
|
||||
}
|
||||
|
||||
deltarad = 1 / (4 * M_PI * (rad + thickness - 1));
|
||||
|
||||
RGBImageLock();
|
||||
|
||||
for (aktrad = 0; aktrad <= (2 * M_PI); aktrad += deltarad)
|
||||
{
|
||||
for (_thick = 0; _thick < thickness; ++_thick)
|
||||
{
|
||||
_x = sin(aktrad) * (radx + _thick) + x1;
|
||||
_y = cos(aktrad) * (rady + _thick) + y1;
|
||||
if (isInImage(_x, _y))
|
||||
{
|
||||
setPixelColor(_x, _y, r, g, b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RGBImageRelease();
|
||||
}
|
||||
|
||||
|
||||
void CImageBasis::drawCircle(int x1, int y1, int rad, int r, int g, int b, int thickness)
|
||||
{
|
||||
float deltarad, aktrad;
|
||||
@@ -349,18 +389,21 @@ void CImageBasis::drawCircle(int x1, int y1, int rad, int r, int g, int b, int t
|
||||
RGBImageLock();
|
||||
|
||||
for (aktrad = 0; aktrad <= (2 * M_PI); aktrad += deltarad)
|
||||
{
|
||||
for (_thick = 0; _thick < thickness; ++_thick)
|
||||
{
|
||||
_x = sin(aktrad) * (rad + _thick) + x1;
|
||||
_y = cos(aktrad) * (rad + _thick) + y1;
|
||||
if (isInImage(_x, _y))
|
||||
{
|
||||
setPixelColor(_x, _y, r, g, b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RGBImageRelease();
|
||||
}
|
||||
|
||||
|
||||
CImageBasis::CImageBasis(string _name)
|
||||
{
|
||||
name = _name;
|
||||
@@ -372,7 +415,6 @@ CImageBasis::CImageBasis(string _name)
|
||||
islocked = false;
|
||||
}
|
||||
|
||||
|
||||
void CImageBasis::CreateEmptyImage(int _width, int _height, int _channels)
|
||||
{
|
||||
bpp = _channels;
|
||||
@@ -382,13 +424,16 @@ void CImageBasis::CreateEmptyImage(int _width, int _height, int _channels)
|
||||
|
||||
RGBImageLock();
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("CreateEmptyImage");
|
||||
#endif
|
||||
|
||||
memsize = width * height * channels;
|
||||
|
||||
rgb_image = (unsigned char*)malloc_psram_heap(std::string(TAG) + "->CImageBasis (" + name + ")", memsize, MALLOC_CAP_SPIRAM);
|
||||
if (name == "TempImage")
|
||||
{
|
||||
rgb_image = (unsigned char *)psram_reserve_shared_tmp_image_memory();
|
||||
}
|
||||
else
|
||||
{
|
||||
rgb_image = (unsigned char *)malloc_psram_heap(std::string(TAG) + "->CImageBasis (" + name + ")", memsize, MALLOC_CAP_SPIRAM);
|
||||
}
|
||||
|
||||
if (rgb_image == NULL)
|
||||
{
|
||||
@@ -398,49 +443,51 @@ void CImageBasis::CreateEmptyImage(int _width, int _height, int _channels)
|
||||
return;
|
||||
}
|
||||
|
||||
stbi_uc* p_source;
|
||||
stbi_uc *p_source;
|
||||
|
||||
for (int x = 0; x < width; ++x)
|
||||
{
|
||||
for (int y = 0; y < height; ++y)
|
||||
{
|
||||
p_source = rgb_image + (channels * (y * width + x));
|
||||
for (int _channels = 0; _channels < channels; ++_channels)
|
||||
p_source[_channels] = (uint8_t) 0;
|
||||
{
|
||||
p_source[_channels] = (uint8_t)0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RGBImageRelease();
|
||||
}
|
||||
|
||||
|
||||
void CImageBasis::EmptyImage()
|
||||
{
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("EmptyImage");
|
||||
#endif
|
||||
|
||||
stbi_uc* p_source;
|
||||
stbi_uc *p_source;
|
||||
|
||||
RGBImageLock();
|
||||
|
||||
for (int x = 0; x < width; ++x)
|
||||
{
|
||||
for (int y = 0; y < height; ++y)
|
||||
{
|
||||
p_source = rgb_image + (channels * (y * width + x));
|
||||
for (int _channels = 0; _channels < channels; ++_channels)
|
||||
p_source[_channels] = (uint8_t) 0;
|
||||
{
|
||||
p_source[_channels] = (uint8_t)0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RGBImageRelease();
|
||||
}
|
||||
|
||||
|
||||
void CImageBasis::LoadFromMemory(stbi_uc *_buffer, int len)
|
||||
{
|
||||
RGBImageLock();
|
||||
|
||||
if (rgb_image != NULL) {
|
||||
if (rgb_image != NULL)
|
||||
{
|
||||
stbi_image_free(rgb_image);
|
||||
//free_psram_heap(std::string(TAG) + "->rgb_image (LoadFromMemory)", rgb_image);
|
||||
}
|
||||
|
||||
rgb_image = stbi_load_from_memory(_buffer, len, &width, &height, &channels, STBI_rgb);
|
||||
@@ -450,7 +497,7 @@ void CImageBasis::LoadFromMemory(stbi_uc *_buffer, int len)
|
||||
if ((width * height * channels) == 0)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Image with size 0 loaded --> reboot to be done! "
|
||||
"Check that your camera module is working and connected properly.");
|
||||
"Check that your camera module is working and connected properly.");
|
||||
LogFile.WriteHeapInfo("LoadFromMemory");
|
||||
|
||||
doReboot();
|
||||
@@ -458,36 +505,49 @@ void CImageBasis::LoadFromMemory(stbi_uc *_buffer, int len)
|
||||
RGBImageRelease();
|
||||
}
|
||||
|
||||
|
||||
void CImageBasis::crop_image(unsigned short cropLeft, unsigned short cropRight, unsigned short cropTop, unsigned short cropBottom)
|
||||
{
|
||||
unsigned int maxTopIndex = cropTop * width * channels;
|
||||
unsigned int minBottomIndex = ((width*height) - (cropBottom * width)) * channels;
|
||||
unsigned int minBottomIndex = ((width * height) - (cropBottom * width)) * channels;
|
||||
unsigned short maxX = width - cropRight; // In pixels
|
||||
unsigned short newWidth = width - cropLeft - cropRight;
|
||||
unsigned short newHeight = height - cropTop - cropBottom;
|
||||
|
||||
unsigned int writeIndex = 0;
|
||||
// Loop over all bytes
|
||||
for (int i = 0; i < width * height * channels; i += channels) {
|
||||
for (int i = 0; i < width * height * channels; i += channels)
|
||||
{
|
||||
// Calculate current X, Y pixel position
|
||||
int x = (i/channels) % width;
|
||||
int x = (i / channels) % width;
|
||||
|
||||
// Crop from the top
|
||||
if (i < maxTopIndex) { continue; }
|
||||
if (i < maxTopIndex)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Crop from the bottom
|
||||
if (i > minBottomIndex) { continue; }
|
||||
if (i > minBottomIndex)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Crop from the left
|
||||
if (x <= cropLeft) { continue; }
|
||||
if (x <= cropLeft)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Crop from the right
|
||||
if (x > maxX) { continue; }
|
||||
if (x > maxX)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we get here, keep the pixels
|
||||
for (int c = 0; c < channels; c++) {
|
||||
rgb_image[writeIndex++] = rgb_image[i+c];
|
||||
for (int c = 0; c < channels; c++)
|
||||
{
|
||||
rgb_image[writeIndex++] = rgb_image[i + c];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -496,7 +556,6 @@ void CImageBasis::crop_image(unsigned short cropLeft, unsigned short cropRight,
|
||||
height = newHeight;
|
||||
}
|
||||
|
||||
|
||||
CImageBasis::CImageBasis(string _name, CImageBasis *_copyfrom)
|
||||
{
|
||||
name = _name;
|
||||
@@ -509,18 +568,15 @@ CImageBasis::CImageBasis(string _name, CImageBasis *_copyfrom)
|
||||
|
||||
RGBImageLock();
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("CImageBasis_copyfrom - Start");
|
||||
#endif
|
||||
|
||||
memsize = width * height * channels;
|
||||
|
||||
|
||||
if (name == "tmpImage") {
|
||||
rgb_image = (unsigned char*)psram_reserve_shared_tmp_image_memory();
|
||||
if (name == "TempImage")
|
||||
{
|
||||
rgb_image = (unsigned char *)psram_reserve_shared_tmp_image_memory();
|
||||
}
|
||||
else {
|
||||
rgb_image = (unsigned char*)malloc_psram_heap(std::string(TAG) + "->CImageBasis (" + name + ")", memsize, MALLOC_CAP_SPIRAM);
|
||||
else
|
||||
{
|
||||
rgb_image = (unsigned char *)malloc_psram_heap(std::string(TAG) + "->CImageBasis (" + name + ")", memsize, MALLOC_CAP_SPIRAM);
|
||||
}
|
||||
|
||||
if (rgb_image == NULL)
|
||||
@@ -533,13 +589,8 @@ CImageBasis::CImageBasis(string _name, CImageBasis *_copyfrom)
|
||||
|
||||
memCopy(_copyfrom->rgb_image, rgb_image, memsize);
|
||||
RGBImageRelease();
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("CImageBasis_copyfrom - done");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
CImageBasis::CImageBasis(string _name, int _width, int _height, int _channels)
|
||||
{
|
||||
name = _name;
|
||||
@@ -552,13 +603,16 @@ CImageBasis::CImageBasis(string _name, int _width, int _height, int _channels)
|
||||
|
||||
RGBImageLock();
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("CImageBasis_width,height,ch - Start");
|
||||
#endif
|
||||
|
||||
memsize = width * height * channels;
|
||||
|
||||
rgb_image = (unsigned char*)malloc_psram_heap(std::string(TAG) + "->CImageBasis (" + name + ")", memsize, MALLOC_CAP_SPIRAM);
|
||||
if (name == "TempImage")
|
||||
{
|
||||
rgb_image = (unsigned char *)psram_reserve_shared_tmp_image_memory();
|
||||
}
|
||||
else
|
||||
{
|
||||
rgb_image = (unsigned char *)malloc_psram_heap(std::string(TAG) + "->CImageBasis (" + name + ")", memsize, MALLOC_CAP_SPIRAM);
|
||||
}
|
||||
|
||||
if (rgb_image == NULL)
|
||||
{
|
||||
@@ -569,13 +623,8 @@ CImageBasis::CImageBasis(string _name, int _width, int _height, int _channels)
|
||||
}
|
||||
|
||||
RGBImageRelease();
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("CImageBasis_width,height,ch - done");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
CImageBasis::CImageBasis(string _name, std::string _image)
|
||||
{
|
||||
name = _name;
|
||||
@@ -584,20 +633,18 @@ CImageBasis::CImageBasis(string _name, std::string _image)
|
||||
externalImage = false;
|
||||
filename = _image;
|
||||
|
||||
if (file_size(_image.c_str()) == 0) {
|
||||
if (file_size(_image.c_str()) == 0)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, _image + " is empty!");
|
||||
return;
|
||||
}
|
||||
|
||||
RGBImageLock();
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("CImageBasis_image - Start");
|
||||
#endif
|
||||
|
||||
rgb_image = stbi_load(_image.c_str(), &width, &height, &bpp, channels);
|
||||
|
||||
if (rgb_image == NULL) {
|
||||
if (rgb_image == NULL)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "CImageBasis-image: Failed to load " + _image + "! Is it corrupted?");
|
||||
LogFile.WriteHeapInfo("CImageBasis-image");
|
||||
RGBImageRelease();
|
||||
@@ -605,25 +652,14 @@ CImageBasis::CImageBasis(string _name, std::string _image)
|
||||
}
|
||||
|
||||
RGBImageRelease();
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
std::string zw = "CImageBasis after load " + _image;
|
||||
ESP_LOGD(TAG, "%s", zw.c_str());
|
||||
ESP_LOGD(TAG, "w %d, h %d, b %d, c %d", width, height, bpp, channels);
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("CImageBasis_image - done");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
bool CImageBasis::ImageOkay(){
|
||||
bool CImageBasis::ImageOkay()
|
||||
{
|
||||
return rgb_image != NULL;
|
||||
}
|
||||
|
||||
|
||||
CImageBasis::CImageBasis(string _name, uint8_t* _rgb_image, int _channels, int _width, int _height, int _bpp)
|
||||
CImageBasis::CImageBasis(string _name, uint8_t *_rgb_image, int _channels, int _width, int _height, int _bpp)
|
||||
{
|
||||
name = _name;
|
||||
islocked = false;
|
||||
@@ -635,58 +671,66 @@ CImageBasis::CImageBasis(string _name, uint8_t* _rgb_image, int _channels, int _
|
||||
externalImage = true;
|
||||
}
|
||||
|
||||
|
||||
void CImageBasis::Negative(void)
|
||||
{
|
||||
RGBImageLock();
|
||||
|
||||
for (int i = 0; i < width * height * channels; i += channels) {
|
||||
for (int c = 0; c < channels; c++) {
|
||||
rgb_image[i+c] = 255 - rgb_image[i+c];
|
||||
for (int i = 0; i < width * height * channels; i += channels)
|
||||
{
|
||||
for (int c = 0; c < channels; c++)
|
||||
{
|
||||
rgb_image[i + c] = 255 - rgb_image[i + c];
|
||||
}
|
||||
}
|
||||
|
||||
RGBImageRelease();
|
||||
}
|
||||
|
||||
|
||||
void CImageBasis::Contrast(float _contrast) //input range [-100..100]
|
||||
// input range [-100..100]
|
||||
void CImageBasis::Contrast(float _contrast)
|
||||
{
|
||||
stbi_uc* p_source;
|
||||
stbi_uc *p_source;
|
||||
|
||||
float contrast = (_contrast/100) + 1; //convert to decimal & shift range: [0..2]
|
||||
float contrast = (_contrast / 100) + 1; // convert to decimal & shift range: [0..2]
|
||||
float intercept = 128 * (1 - contrast);
|
||||
|
||||
RGBImageLock();
|
||||
|
||||
|
||||
for (int x = 0; x < width; ++x)
|
||||
{
|
||||
for (int y = 0; y < height; ++y)
|
||||
{
|
||||
p_source = rgb_image + (channels * (y * width + x));
|
||||
for (int _channels = 0; _channels < channels; ++_channels)
|
||||
p_source[_channels] = (uint8_t) std::min(255, std::max(0, (int) (p_source[_channels] * contrast + intercept)));
|
||||
{
|
||||
p_source[_channels] = (uint8_t)std::min(255, std::max(0, (int)(p_source[_channels] * contrast + intercept)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RGBImageRelease();
|
||||
}
|
||||
|
||||
|
||||
CImageBasis::~CImageBasis()
|
||||
{
|
||||
RGBImageLock();
|
||||
|
||||
|
||||
if (!externalImage) {
|
||||
if (name == "tmpImage") { // This image should be placed in the shared part of PSRAM
|
||||
if (!externalImage)
|
||||
{
|
||||
if (name == "TempImage")
|
||||
{
|
||||
// This image should be placed in the shared part of PSRAM
|
||||
psram_free_shared_temp_image_memory();
|
||||
}
|
||||
else { // All other images are much smaller and can go into the normal PSRAM region
|
||||
//stbi_image_free(rgb_image);
|
||||
if (memsize == 0) {
|
||||
else
|
||||
{
|
||||
// All other images are much smaller and can go into the normal PSRAM region
|
||||
if (memsize == 0)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Not freeing (" + name + " as there was never PSRAM allocated for it)");
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
free_psram_heap(std::string(TAG) + "->CImageBasis (" + name + ", " + to_string(memsize) + ")", rgb_image);
|
||||
}
|
||||
}
|
||||
@@ -695,15 +739,15 @@ CImageBasis::~CImageBasis()
|
||||
RGBImageRelease();
|
||||
}
|
||||
|
||||
|
||||
void CImageBasis::SaveToFile(std::string _imageout)
|
||||
{
|
||||
string typ = getFileType(_imageout);
|
||||
string typ = get_file_type(_imageout);
|
||||
|
||||
RGBImageLock();
|
||||
|
||||
if ((typ == "jpg") || (typ == "JPG")) // CAUTION PROBLEMATIC IN ESP32
|
||||
if ((typ == "jpg") || (typ == "JPG"))
|
||||
{
|
||||
// CAUTION PROBLEMATIC IN ESP32
|
||||
stbi_write_jpg(_imageout.c_str(), width, height, channels, rgb_image, 0);
|
||||
}
|
||||
|
||||
@@ -716,27 +760,25 @@ void CImageBasis::SaveToFile(std::string _imageout)
|
||||
RGBImageRelease();
|
||||
}
|
||||
|
||||
|
||||
void CImageBasis::Resize(int _new_dx, int _new_dy)
|
||||
{
|
||||
memsize = _new_dx * _new_dy * channels;
|
||||
uint8_t* odata = (unsigned char*)malloc_psram_heap(std::string(TAG) + "->odata", memsize, MALLOC_CAP_SPIRAM);
|
||||
uint8_t *temp_image = (unsigned char *)malloc_psram_heap(std::string(TAG) + "->temp_image", memsize, MALLOC_CAP_SPIRAM);
|
||||
|
||||
RGBImageLock();
|
||||
|
||||
stbir_resize_uint8(rgb_image, width, height, 0, odata, _new_dx, _new_dy, 0, channels);
|
||||
stbir_resize_uint8(rgb_image, width, height, 0, temp_image, _new_dx, _new_dy, 0, channels);
|
||||
|
||||
rgb_image = (unsigned char*)malloc_psram_heap(std::string(TAG) + "->CImageBasis Resize (" + name + ")", memsize, MALLOC_CAP_SPIRAM);
|
||||
memCopy(odata, rgb_image, memsize);
|
||||
rgb_image = (unsigned char *)malloc_psram_heap(std::string(TAG) + "->CImageBasis Resize (" + name + ")", memsize, MALLOC_CAP_SPIRAM);
|
||||
memCopy(temp_image, rgb_image, memsize);
|
||||
width = _new_dx;
|
||||
height = _new_dy;
|
||||
|
||||
free_psram_heap(std::string(TAG) + "->odata", odata);
|
||||
free_psram_heap(std::string(TAG) + "->temp_image", temp_image);
|
||||
|
||||
RGBImageRelease();
|
||||
}
|
||||
|
||||
|
||||
void CImageBasis::Resize(int _new_dx, int _new_dy, CImageBasis *_target)
|
||||
{
|
||||
if ((_target->height != _new_dy) || (_target->width != _new_dx) || (_target->channels != channels))
|
||||
@@ -747,9 +789,8 @@ void CImageBasis::Resize(int _new_dx, int _new_dy, CImageBasis *_target)
|
||||
|
||||
RGBImageLock();
|
||||
|
||||
uint8_t* odata = _target->rgb_image;
|
||||
stbir_resize_uint8(rgb_image, width, height, 0, odata, _new_dx, _new_dy, 0, channels);
|
||||
uint8_t *temp_image = _target->rgb_image;
|
||||
stbir_resize_uint8(rgb_image, width, height, 0, temp_image, _new_dx, _new_dy, 0, channels);
|
||||
|
||||
RGBImageRelease();
|
||||
}
|
||||
|
||||
|
||||
@@ -3,16 +3,17 @@
|
||||
#ifndef CIMAGEBASIS_H
|
||||
#define CIMAGEBASIS_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "defines.h"
|
||||
|
||||
#include <string>
|
||||
#include <esp_http_server.h>
|
||||
|
||||
#include "../../include/defines.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <esp_http_server.h>
|
||||
|
||||
#include "../stb/stb_image.h"
|
||||
#include "../stb/stb_image_write.h"
|
||||
// #include "../stb/deprecated/stb_image_resize.h"
|
||||
#include "../stb/stb_image_resize.h"
|
||||
|
||||
#include "esp_heap_caps.h"
|
||||
@@ -23,74 +24,69 @@ struct ImageData
|
||||
size_t size = 0;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class CImageBasis
|
||||
{
|
||||
protected:
|
||||
bool externalImage;
|
||||
std::string filename;
|
||||
std::string name; // Just used for diagnostics
|
||||
int memsize = 0;
|
||||
protected:
|
||||
bool externalImage;
|
||||
std::string filename;
|
||||
std::string name; // Just used for diagnostics
|
||||
int memsize = 0;
|
||||
|
||||
void memCopy(uint8_t* _source, uint8_t* _target, int _size);
|
||||
bool isInImage(int x, int y);
|
||||
void memCopy(uint8_t *_source, uint8_t *_target, int _size);
|
||||
bool isInImage(int x, int y);
|
||||
|
||||
bool islocked;
|
||||
bool islocked;
|
||||
|
||||
public:
|
||||
uint8_t* rgb_image = NULL;
|
||||
int channels;
|
||||
int width, height, bpp;
|
||||
public:
|
||||
uint8_t *rgb_image = NULL;
|
||||
int channels;
|
||||
int width, height, bpp;
|
||||
|
||||
uint8_t * RGBImageLock(int _waitmaxsec = 60);
|
||||
void RGBImageRelease();
|
||||
uint8_t * RGBImageGet();
|
||||
uint8_t *RGBImageLock(int _waitmaxsec = 60);
|
||||
void RGBImageRelease();
|
||||
uint8_t *RGBImageGet();
|
||||
|
||||
int getWidth(){return this->width;};
|
||||
int getHeight(){return this->height;};
|
||||
int getChannels(){return this->channels;};
|
||||
void drawRect(int x, int y, int dx, int dy, int r = 255, int g = 255, int b = 255, int thickness = 1);
|
||||
void drawLine(int x1, int y1, int x2, int y2, int r, int g, int b, int thickness = 1);
|
||||
void drawCircle(int x1, int y1, int rad, int r, int g, int b, int thickness = 1);
|
||||
void drawEllipse(int x1, int y1, int radx, int rady, int r, int g, int b, int thickness = 1);
|
||||
int getWidth() { return this->width; };
|
||||
int getHeight() { return this->height; };
|
||||
int getChannels() { return this->channels; };
|
||||
void drawRect(int x, int y, int dx, int dy, int r = 255, int g = 255, int b = 255, int thickness = 1);
|
||||
void drawLine(int x1, int y1, int x2, int y2, int r, int g, int b, int thickness = 1);
|
||||
void drawCircle(int x1, int y1, int rad, int r, int g, int b, int thickness = 1);
|
||||
void drawEllipse(int x1, int y1, int radx, int rady, int r, int g, int b, int thickness = 1);
|
||||
|
||||
void setPixelColor(int x, int y, int r, int g, int b);
|
||||
void Negative(void);
|
||||
void Contrast(float _contrast);
|
||||
bool ImageOkay();
|
||||
bool CopyFromMemory(uint8_t* _source, int _size);
|
||||
void setPixelColor(int x, int y, int r, int g, int b);
|
||||
void Negative(void);
|
||||
void Contrast(float _contrast);
|
||||
bool ImageOkay();
|
||||
bool CopyFromMemory(uint8_t *_source, int _size);
|
||||
|
||||
void SetIndepended(){externalImage = false;};
|
||||
void SetIndepended() { externalImage = false; };
|
||||
|
||||
void CreateEmptyImage(int _width, int _height, int _channels);
|
||||
void EmptyImage();
|
||||
void CreateEmptyImage(int _width, int _height, int _channels);
|
||||
void EmptyImage();
|
||||
|
||||
CImageBasis(std::string name);
|
||||
CImageBasis(std::string name, std::string _image);
|
||||
CImageBasis(std::string name, uint8_t *_rgb_image, int _channels, int _width, int _height, int _bpp);
|
||||
CImageBasis(std::string name, int _width, int _height, int _channels);
|
||||
CImageBasis(std::string name, CImageBasis *_copyfrom);
|
||||
|
||||
CImageBasis(std::string name);
|
||||
CImageBasis(std::string name, std::string _image);
|
||||
CImageBasis(std::string name, uint8_t* _rgb_image, int _channels, int _width, int _height, int _bpp);
|
||||
CImageBasis(std::string name, int _width, int _height, int _channels);
|
||||
CImageBasis(std::string name, CImageBasis *_copyfrom);
|
||||
void Resize(int _new_dx, int _new_dy);
|
||||
void Resize(int _new_dx, int _new_dy, CImageBasis *_target);
|
||||
void crop_image(unsigned short cropLeft, unsigned short cropRight, unsigned short cropTop, unsigned short cropBottom);
|
||||
|
||||
void Resize(int _new_dx, int _new_dy);
|
||||
void Resize(int _new_dx, int _new_dy, CImageBasis *_target);
|
||||
void crop_image(unsigned short cropLeft, unsigned short cropRight, unsigned short cropTop, unsigned short cropBottom);
|
||||
void LoadFromMemory(stbi_uc *_buffer, int len);
|
||||
|
||||
void LoadFromMemory(stbi_uc *_buffer, int len);
|
||||
ImageData *writeToMemoryAsJPG(const int quality = 90);
|
||||
void writeToMemoryAsJPG(ImageData *ii, const int quality = 90);
|
||||
|
||||
ImageData* writeToMemoryAsJPG(const int quality = 90);
|
||||
void writeToMemoryAsJPG(ImageData* ii, const int quality = 90);
|
||||
esp_err_t SendJPGtoHTTP(httpd_req_t *req, const int quality = 90);
|
||||
|
||||
esp_err_t SendJPGtoHTTP(httpd_req_t *req, const int quality = 90);
|
||||
uint8_t GetPixelColor(int x, int y, int ch);
|
||||
|
||||
uint8_t GetPixelColor(int x, int y, int ch);
|
||||
~CImageBasis();
|
||||
|
||||
~CImageBasis();
|
||||
|
||||
void SaveToFile(std::string _imageout);
|
||||
void SaveToFile(std::string _imageout);
|
||||
};
|
||||
|
||||
|
||||
#endif //CIMAGEBASIS_H
|
||||
|
||||
#endif // CIMAGEBASIS_H
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
|
||||
|
||||
idf_component_register(SRCS ${app_sources}
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES jomjol_helper jomjol_logfile esp_http_server jomjol_fileserver_ota)
|
||||
INCLUDE_DIRS "." "../../include"
|
||||
REQUIRES jomjol_helper jomjol_logfile jomjol_flowcontroll esp_http_server jomjol_fileserver_ota)
|
||||
|
||||
|
||||
|
||||
@@ -31,10 +31,13 @@ void CRotateImage::Rotate(float _angle, int _centerx, int _centery)
|
||||
{
|
||||
org_width = width;
|
||||
org_height = height;
|
||||
|
||||
height = org_width;
|
||||
width = org_height;
|
||||
x_center = x_center - (org_width/2) + (org_height/2);
|
||||
y_center = y_center + (org_width/2) - (org_height/2);
|
||||
|
||||
x_center = x_center - (org_width / 2) + (org_height / 2);
|
||||
y_center = y_center + (org_width / 2) - (org_height / 2);
|
||||
|
||||
if (ImageOrg)
|
||||
{
|
||||
ImageOrg->height = height;
|
||||
@@ -57,32 +60,32 @@ void CRotateImage::Rotate(float _angle, int _centerx, int _centery)
|
||||
|
||||
if (doflip)
|
||||
{
|
||||
m[0][2] = m[0][2] + (org_width/2) - (org_height/2);
|
||||
m[1][2] = m[1][2] - (org_width/2) + (org_height/2);
|
||||
m[0][2] = m[0][2] + (org_width / 2) - (org_height / 2);
|
||||
m[1][2] = m[1][2] - (org_width / 2) + (org_height / 2);
|
||||
}
|
||||
|
||||
int memsize = width * height * channels;
|
||||
uint8_t* odata;
|
||||
uint8_t *temp_image;
|
||||
if (ImageTMP)
|
||||
{
|
||||
odata = ImageTMP->RGBImageLock();
|
||||
temp_image = ImageTMP->RGBImageLock();
|
||||
}
|
||||
else
|
||||
{
|
||||
odata = (unsigned char*)malloc_psram_heap(std::string(TAG) + "->odata", memsize, MALLOC_CAP_SPIRAM);
|
||||
temp_image = (unsigned char *)malloc_psram_heap(std::string(TAG) + "->temp_image", memsize, MALLOC_CAP_SPIRAM);
|
||||
}
|
||||
|
||||
|
||||
int x_source, y_source;
|
||||
stbi_uc* p_target;
|
||||
stbi_uc* p_source;
|
||||
stbi_uc *p_target;
|
||||
stbi_uc *p_source;
|
||||
|
||||
RGBImageLock();
|
||||
|
||||
for (int x = 0; x < width; ++x)
|
||||
{
|
||||
for (int y = 0; y < height; ++y)
|
||||
{
|
||||
p_target = odata + (channels * (y * width + x));
|
||||
p_target = temp_image + (channels * (y * width + x));
|
||||
|
||||
x_source = int(m[0][0] * x + m[0][1] * y);
|
||||
y_source = int(m[1][0] * x + m[1][1] * y);
|
||||
@@ -93,30 +96,44 @@ void CRotateImage::Rotate(float _angle, int _centerx, int _centery)
|
||||
if ((x_source >= 0) && (x_source < org_width) && (y_source >= 0) && (y_source < org_height))
|
||||
{
|
||||
p_source = rgb_image + (channels * (y_source * org_width + x_source));
|
||||
|
||||
for (int _channels = 0; _channels < channels; ++_channels)
|
||||
{
|
||||
p_target[_channels] = p_source[_channels];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int _channels = 0; _channels < channels; ++_channels)
|
||||
p_target[_channels] = 255;
|
||||
{
|
||||
// p_target[_channels] = 255; // nicht vorhandene Pixel werden Weiß gemacht
|
||||
// p_target[_channels] = 0; // nicht vorhandene Pixel werden Schwarz gemacht
|
||||
// p_target[_channels] = 128; // nicht vorhandene Pixel werden Grau gemacht
|
||||
p_target[_channels] = pixel_fill_color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// memcpy(rgb_image, odata, memsize);
|
||||
memCopy(odata, rgb_image, memsize);
|
||||
|
||||
if (!ImageTMP)
|
||||
{
|
||||
free_psram_heap(std::string(TAG) + "->odata", odata);
|
||||
}
|
||||
|
||||
memCopy(temp_image, rgb_image, memsize);
|
||||
|
||||
if (ImageTMP)
|
||||
{
|
||||
ImageTMP->RGBImageRelease();
|
||||
}
|
||||
else
|
||||
{
|
||||
free_psram_heap(std::string(TAG) + "->temp_image", temp_image);
|
||||
}
|
||||
|
||||
RGBImageRelease();
|
||||
}
|
||||
|
||||
|
||||
void CRotateImage::Rotate(float _angle)
|
||||
{
|
||||
// ESP_LOGD(TAG, "width %d, height %d", width, height);
|
||||
Rotate(_angle, width / 2, height / 2);
|
||||
}
|
||||
|
||||
void CRotateImage::RotateAntiAliasing(float _angle, int _centerx, int _centery)
|
||||
{
|
||||
@@ -131,10 +148,13 @@ void CRotateImage::RotateAntiAliasing(float _angle, int _centerx, int _centery)
|
||||
{
|
||||
org_width = width;
|
||||
org_height = height;
|
||||
|
||||
height = org_width;
|
||||
width = org_height;
|
||||
x_center = x_center - (org_width/2) + (org_height/2);
|
||||
y_center = y_center + (org_width/2) - (org_height/2);
|
||||
|
||||
x_center = x_center - (org_width / 2) + (org_height / 2);
|
||||
y_center = y_center + (org_width / 2) - (org_height / 2);
|
||||
|
||||
if (ImageOrg)
|
||||
{
|
||||
ImageOrg->height = height;
|
||||
@@ -157,34 +177,34 @@ void CRotateImage::RotateAntiAliasing(float _angle, int _centerx, int _centery)
|
||||
|
||||
if (doflip)
|
||||
{
|
||||
m[0][2] = m[0][2] + (org_width/2) - (org_height/2);
|
||||
m[1][2] = m[1][2] - (org_width/2) + (org_height/2);
|
||||
m[0][2] = m[0][2] + (org_width / 2) - (org_height / 2);
|
||||
m[1][2] = m[1][2] - (org_width / 2) + (org_height / 2);
|
||||
}
|
||||
|
||||
int memsize = width * height * channels;
|
||||
uint8_t* odata;
|
||||
uint8_t *temp_image;
|
||||
if (ImageTMP)
|
||||
{
|
||||
odata = ImageTMP->RGBImageLock();
|
||||
temp_image = ImageTMP->RGBImageLock();
|
||||
}
|
||||
else
|
||||
{
|
||||
odata = (unsigned char*)malloc_psram_heap(std::string(TAG) + "->odata", memsize, MALLOC_CAP_SPIRAM);
|
||||
temp_image = (unsigned char *)malloc_psram_heap(std::string(TAG) + "->temp_image", memsize, MALLOC_CAP_SPIRAM);
|
||||
}
|
||||
|
||||
|
||||
int x_source_1, y_source_1, x_source_2, y_source_2;
|
||||
float x_source, y_source;
|
||||
float quad_ul, quad_ur, quad_ol, quad_or;
|
||||
stbi_uc* p_target;
|
||||
stbi_uc *p_target;
|
||||
stbi_uc *p_source_ul, *p_source_ur, *p_source_ol, *p_source_or;
|
||||
|
||||
RGBImageLock();
|
||||
|
||||
for (int x = 0; x < width; ++x)
|
||||
{
|
||||
for (int y = 0; y < height; ++y)
|
||||
{
|
||||
p_target = odata + (channels * (y * width + x));
|
||||
p_target = temp_image + (channels * (y * width + x));
|
||||
|
||||
x_source = (m[0][0] * x + m[0][1] * y);
|
||||
y_source = (m[1][0] * x + m[1][1] * y);
|
||||
@@ -198,10 +218,9 @@ void CRotateImage::RotateAntiAliasing(float _angle, int _centerx, int _centery)
|
||||
y_source_2 = y_source_1 + 1;
|
||||
|
||||
quad_ul = (x_source_2 - x_source) * (y_source_2 - y_source);
|
||||
quad_ur = (1- (x_source_2 - x_source)) * (y_source_2 - y_source);
|
||||
quad_or = (x_source_2 - x_source) * (1-(y_source_2 - y_source));
|
||||
quad_ol = (1- (x_source_2 - x_source)) * (1-(y_source_2 - y_source));
|
||||
|
||||
quad_ur = (1 - (x_source_2 - x_source)) * (y_source_2 - y_source);
|
||||
quad_or = (x_source_2 - x_source) * (1 - (y_source_2 - y_source));
|
||||
quad_ol = (1 - (x_source_2 - x_source)) * (1 - (y_source_2 - y_source));
|
||||
|
||||
if ((x_source_1 >= 0) && (x_source_2 < org_width) && (y_source_1 >= 0) && (y_source_2 < org_height))
|
||||
{
|
||||
@@ -209,72 +228,70 @@ void CRotateImage::RotateAntiAliasing(float _angle, int _centerx, int _centery)
|
||||
p_source_ur = rgb_image + (channels * (y_source_1 * org_width + x_source_2));
|
||||
p_source_or = rgb_image + (channels * (y_source_2 * org_width + x_source_1));
|
||||
p_source_ol = rgb_image + (channels * (y_source_2 * org_width + x_source_2));
|
||||
|
||||
for (int _channels = 0; _channels < channels; ++_channels)
|
||||
{
|
||||
p_target[_channels] = (int)((float)p_source_ul[_channels] * quad_ul
|
||||
+ (float)p_source_ur[_channels] * quad_ur
|
||||
+ (float)p_source_or[_channels] * quad_or
|
||||
+ (float)p_source_ol[_channels] * quad_ol);
|
||||
p_target[_channels] = (int)((float)p_source_ul[_channels] * quad_ul + (float)p_source_ur[_channels] * quad_ur + (float)p_source_or[_channels] * quad_or + (float)p_source_ol[_channels] * quad_ol);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int _channels = 0; _channels < channels; ++_channels)
|
||||
p_target[_channels] = 255;
|
||||
{
|
||||
// p_target[_channels] = 255; // nicht vorhandene Pixel werden Weiß gemacht
|
||||
// p_target[_channels] = 0; // nicht vorhandene Pixel werden Schwarz gemacht
|
||||
// p_target[_channels] = 128; // nicht vorhandene Pixel werden Grau gemacht
|
||||
p_target[_channels] = pixel_fill_color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// memcpy(rgb_image, odata, memsize);
|
||||
memCopy(odata, rgb_image, memsize);
|
||||
|
||||
if (!ImageTMP)
|
||||
{
|
||||
free_psram_heap(std::string(TAG) + "->odata", odata);
|
||||
}
|
||||
|
||||
memCopy(temp_image, rgb_image, memsize);
|
||||
|
||||
if (ImageTMP)
|
||||
{
|
||||
ImageTMP->RGBImageRelease();
|
||||
}
|
||||
else
|
||||
{
|
||||
free_psram_heap(std::string(TAG) + "->temp_image", temp_image);
|
||||
}
|
||||
|
||||
RGBImageRelease();
|
||||
}
|
||||
|
||||
|
||||
void CRotateImage::Rotate(float _angle)
|
||||
{
|
||||
// ESP_LOGD(TAG, "width %d, height %d", width, height);
|
||||
Rotate(_angle, width / 2, height / 2);
|
||||
}
|
||||
|
||||
void CRotateImage::RotateAntiAliasing(float _angle)
|
||||
{
|
||||
// ESP_LOGD(TAG, "width %d, height %d", width, height);
|
||||
// ESP_LOGD(TAG, "width %d, height %d", width, height);
|
||||
RotateAntiAliasing(_angle, width / 2, height / 2);
|
||||
}
|
||||
|
||||
void CRotateImage::Translate(int _dx, int _dy)
|
||||
{
|
||||
int memsize = width * height * channels;
|
||||
uint8_t* odata;
|
||||
uint8_t *temp_image;
|
||||
|
||||
if (ImageTMP)
|
||||
{
|
||||
odata = ImageTMP->RGBImageLock();
|
||||
temp_image = ImageTMP->RGBImageLock();
|
||||
}
|
||||
else
|
||||
{
|
||||
odata = (unsigned char*)malloc_psram_heap(std::string(TAG) + "->odata", memsize, MALLOC_CAP_SPIRAM);
|
||||
temp_image = (unsigned char *)malloc_psram_heap(std::string(TAG) + "->temp_image", memsize, MALLOC_CAP_SPIRAM);
|
||||
}
|
||||
|
||||
|
||||
|
||||
int x_source, y_source;
|
||||
stbi_uc* p_target;
|
||||
stbi_uc* p_source;
|
||||
stbi_uc *p_target;
|
||||
stbi_uc *p_source;
|
||||
|
||||
RGBImageLock();
|
||||
|
||||
for (int x = 0; x < width; ++x)
|
||||
{
|
||||
for (int y = 0; y < height; ++y)
|
||||
{
|
||||
p_target = odata + (channels * (y * width + x));
|
||||
p_target = temp_image + (channels * (y * width + x));
|
||||
|
||||
x_source = x - _dx;
|
||||
y_source = y - _dy;
|
||||
@@ -282,28 +299,35 @@ void CRotateImage::Translate(int _dx, int _dy)
|
||||
if ((x_source >= 0) && (x_source < width) && (y_source >= 0) && (y_source < height))
|
||||
{
|
||||
p_source = rgb_image + (channels * (y_source * width + x_source));
|
||||
|
||||
for (int _channels = 0; _channels < channels; ++_channels)
|
||||
{
|
||||
p_target[_channels] = p_source[_channels];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int _channels = 0; _channels < channels; ++_channels)
|
||||
p_target[_channels] = 255;
|
||||
{
|
||||
// p_target[_channels] = 255; // nicht vorhandene Pixel werden Weiß gemacht
|
||||
// p_target[_channels] = 0; // nicht vorhandene Pixel werden Schwarz gemacht
|
||||
// p_target[_channels] = 128; // nicht vorhandene Pixel werden Grau gemacht
|
||||
p_target[_channels] = pixel_fill_color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// memcpy(rgb_image, odata, memsize);
|
||||
memCopy(odata, rgb_image, memsize);
|
||||
if (!ImageTMP)
|
||||
{
|
||||
free_psram_heap(std::string(TAG) + "->odata", odata);
|
||||
}
|
||||
|
||||
memCopy(temp_image, rgb_image, memsize);
|
||||
|
||||
if (ImageTMP)
|
||||
{
|
||||
ImageTMP->RGBImageRelease();
|
||||
}
|
||||
else
|
||||
{
|
||||
free_psram_heap(std::string(TAG) + "->temp_image", temp_image);
|
||||
}
|
||||
|
||||
RGBImageRelease();
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -5,24 +5,36 @@
|
||||
|
||||
#include "CImageBasis.h"
|
||||
|
||||
|
||||
class CRotateImage: public CImageBasis
|
||||
class CRotateImage : public CImageBasis
|
||||
{
|
||||
public:
|
||||
CImageBasis *ImageTMP, *ImageOrg;
|
||||
int pixel_fill_color = 216; // Gray
|
||||
bool doflip;
|
||||
|
||||
public:
|
||||
CImageBasis *ImageTMP, *ImageOrg;
|
||||
bool doflip;
|
||||
CRotateImage(std::string name, std::string _image, bool _flip = false) : CImageBasis(name, _image) {ImageTMP = NULL; ImageOrg = NULL; doflip = _flip;};
|
||||
CRotateImage(std::string name, uint8_t* _rgb_image, int _channels, int _width, int _height, int _bpp, bool _flip = false) : CImageBasis(name, _rgb_image, _channels, _width, _height, _bpp) {ImageTMP = NULL; ImageOrg = NULL; doflip = _flip;};
|
||||
CRotateImage(std::string name, CImageBasis *_org, CImageBasis *_temp, bool _flip = false);
|
||||
CRotateImage(std::string name, std::string _image, bool _flip = false) : CImageBasis(name, _image)
|
||||
{
|
||||
ImageTMP = NULL;
|
||||
ImageOrg = NULL;
|
||||
doflip = _flip;
|
||||
};
|
||||
|
||||
void Rotate(float _angle);
|
||||
void RotateAntiAliasing(float _angle);
|
||||
CRotateImage(std::string name, uint8_t *_rgb_image, int _channels, int _width, int _height, int _bpp, bool _flip = false) : CImageBasis(name, _rgb_image, _channels, _width, _height, _bpp)
|
||||
{
|
||||
ImageTMP = NULL;
|
||||
ImageOrg = NULL;
|
||||
doflip = _flip;
|
||||
};
|
||||
|
||||
void Rotate(float _angle, int _centerx, int _centery);
|
||||
void RotateAntiAliasing(float _angle, int _centerx, int _centery);
|
||||
CRotateImage(std::string name, CImageBasis *_org, CImageBasis *_temp, bool _flip = false);
|
||||
|
||||
void Translate(int _dx, int _dy);
|
||||
void Rotate(float _angle);
|
||||
void RotateAntiAliasing(float _angle);
|
||||
|
||||
void Rotate(float _angle, int _centerx, int _centery);
|
||||
void RotateAntiAliasing(float _angle, int _centerx, int _centery);
|
||||
|
||||
void Translate(int _dx, int _dy);
|
||||
};
|
||||
|
||||
#endif //CROTATEIMAGE_H
|
||||
#endif // CROTATEIMAGE_H
|
||||
|
||||
@@ -1,23 +1,22 @@
|
||||
#include <stdint.h>
|
||||
#include "defines.h"
|
||||
|
||||
#include <string>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "psram.h"
|
||||
|
||||
#include "../../include/defines.h"
|
||||
|
||||
|
||||
#define USE_SHARED_PSRAM_FOR_STBI
|
||||
|
||||
#ifdef USE_SHARED_PSRAM_FOR_STBI
|
||||
#define STBI_MALLOC(sz) psram_reserve_shared_stbi_memory(sz)
|
||||
#define STBI_REALLOC(p,newsz) psram_reallocate_shared_stbi_memory(p, newsz)
|
||||
#define STBI_FREE(p) psram_free_shared_stbi_memory(p)
|
||||
#define STBI_MALLOC(sz) psram_reserve_shared_stbi_memory(sz)
|
||||
#define STBI_REALLOC(p, newsz) psram_reallocate_shared_stbi_memory(p, newsz)
|
||||
#define STBI_FREE(p) psram_free_shared_stbi_memory(p)
|
||||
#else // Use normal PSRAM
|
||||
#define STBI_MALLOC(sz) malloc_psram_heap("STBI", sz, MALLOC_CAP_SPIRAM)
|
||||
#define STBI_REALLOC(p,newsz) realloc_psram_heap("STBI", p, newsz, MALLOC_CAP_SPIRAM)
|
||||
#define STBI_FREE(p) free_psram_heap("STBI", p)
|
||||
#define STBI_MALLOC(sz) malloc_psram_heap("STBI", sz, MALLOC_CAP_SPIRAM)
|
||||
#define STBI_REALLOC(p, newsz) realloc_psram_heap("STBI", p, newsz, MALLOC_CAP_SPIRAM)
|
||||
#define STBI_FREE(p) free_psram_heap("STBI", p)
|
||||
#endif
|
||||
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include "../stb/stb_image.h"
|
||||
|
||||
@@ -25,4 +24,5 @@
|
||||
#include "../stb/stb_image_write.h"
|
||||
|
||||
#define STB_IMAGE_RESIZE_IMPLEMENTATION
|
||||
// #include "../stb/deprecated/stb_image_resize.h"
|
||||
#include "../stb/stb_image_resize.h"
|
||||
@@ -1,7 +1,7 @@
|
||||
FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
|
||||
|
||||
idf_component_register(SRCS ${app_sources}
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES esp_http_client jomjol_logfile)
|
||||
INCLUDE_DIRS "." "../../include"
|
||||
REQUIRES esp_http_client jomjol_logfile jomjol_helper)
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#ifdef ENABLE_INFLUXDB
|
||||
#include "defines.h"
|
||||
|
||||
#include "interface_influxdb.h"
|
||||
|
||||
#include "esp_log.h"
|
||||
@@ -6,7 +7,6 @@
|
||||
#include "ClassLogFile.h"
|
||||
#include "esp_http_client.h"
|
||||
#include "time_sntp.h"
|
||||
#include "../../include/defines.h"
|
||||
|
||||
static const char *TAG = "INFLUXDB";
|
||||
|
||||
@@ -18,7 +18,6 @@ static const char *TAG = "INFLUXDB";
|
||||
*/
|
||||
char response_buffer[MAX_HTTP_OUTPUT_BUFFER] = {0};
|
||||
|
||||
|
||||
/**
|
||||
* @brief HTTP event handler callback function.
|
||||
*
|
||||
@@ -39,39 +38,37 @@ char response_buffer[MAX_HTTP_OUTPUT_BUFFER] = {0};
|
||||
*/
|
||||
static esp_err_t http_event_handler(esp_http_client_event_t *evt)
|
||||
{
|
||||
switch(evt->event_id)
|
||||
switch (evt->event_id)
|
||||
{
|
||||
case HTTP_EVENT_ERROR:
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Client Error encountered");
|
||||
break;
|
||||
case HTTP_EVENT_ON_CONNECTED:
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Client connected");
|
||||
ESP_LOGI(TAG, "HTTP Client Connected");
|
||||
break;
|
||||
case HTTP_EVENT_HEADERS_SENT:
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Client sent all request headers");
|
||||
break;
|
||||
case HTTP_EVENT_ON_HEADER:
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Header: key=" + std::string(evt->header_key) + ", value=" + std::string(evt->header_value));
|
||||
break;
|
||||
case HTTP_EVENT_ON_DATA:
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Client data recevied: len=" + std::to_string(evt->data_len));
|
||||
break;
|
||||
case HTTP_EVENT_ON_FINISH:
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Client finished");
|
||||
break;
|
||||
case HTTP_EVENT_DISCONNECTED:
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Client Disconnected");
|
||||
break;
|
||||
case HTTP_EVENT_REDIRECT:
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Redirect");
|
||||
break;
|
||||
case HTTP_EVENT_ERROR:
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Client Error encountered");
|
||||
break;
|
||||
case HTTP_EVENT_ON_CONNECTED:
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Client connected");
|
||||
ESP_LOGI(TAG, "HTTP Client Connected");
|
||||
break;
|
||||
case HTTP_EVENT_HEADERS_SENT:
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Client sent all request headers");
|
||||
break;
|
||||
case HTTP_EVENT_ON_HEADER:
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Header: key=" + std::string(evt->header_key) + ", value=" + std::string(evt->header_value));
|
||||
break;
|
||||
case HTTP_EVENT_ON_DATA:
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Client data recevied: len=" + std::to_string(evt->data_len));
|
||||
break;
|
||||
case HTTP_EVENT_ON_FINISH:
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Client finished");
|
||||
break;
|
||||
case HTTP_EVENT_DISCONNECTED:
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Client Disconnected");
|
||||
break;
|
||||
case HTTP_EVENT_REDIRECT:
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "HTTP Redirect");
|
||||
break;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Initializes the InfluxDB connection with version 1 settings.
|
||||
*
|
||||
@@ -82,7 +79,8 @@ static esp_err_t http_event_handler(esp_http_client_event_t *evt)
|
||||
* @param _user The username for authentication.
|
||||
* @param _password The password for authentication.
|
||||
*/
|
||||
void InfluxDB::InfluxDBInitV1(std::string _influxDBURI, std::string _database, std::string _user, std::string _password) {
|
||||
void InfluxDB::InfluxDBInitV1(std::string _influxDBURI, std::string _database, std::string _user, std::string _password)
|
||||
{
|
||||
version = INFLUXDB_V1;
|
||||
influxDBURI = _influxDBURI;
|
||||
database = _database;
|
||||
@@ -101,7 +99,8 @@ void InfluxDB::InfluxDBInitV1(std::string _influxDBURI, std::string _database, s
|
||||
* @param _org The organization name associated with the bucket.
|
||||
* @param _token The authentication token for accessing the InfluxDB server.
|
||||
*/
|
||||
void InfluxDB::InfluxDBInitV2(std::string _influxDBURI, std::string _bucket, std::string _org, std::string _token) {
|
||||
void InfluxDB::InfluxDBInitV2(std::string _influxDBURI, std::string _bucket, std::string _org, std::string _token)
|
||||
{
|
||||
version = INFLUXDB_V2;
|
||||
influxDBURI = _influxDBURI;
|
||||
bucket = _bucket;
|
||||
@@ -121,7 +120,8 @@ void InfluxDB::InfluxDBInitV2(std::string _influxDBURI, std::string _bucket, std
|
||||
* @param None
|
||||
* @return None
|
||||
*/
|
||||
void InfluxDB::connectHTTP() {
|
||||
void InfluxDB::connectHTTP()
|
||||
{
|
||||
esp_http_client_config_t config = {};
|
||||
|
||||
config.url = influxDBURI.c_str();
|
||||
@@ -129,35 +129,39 @@ void InfluxDB::connectHTTP() {
|
||||
config.buffer_size = MAX_HTTP_OUTPUT_BUFFER;
|
||||
config.user_data = response_buffer;
|
||||
|
||||
|
||||
switch (version) {
|
||||
case INFLUXDB_V1:
|
||||
config.auth_type = HTTP_AUTH_TYPE_BASIC;
|
||||
config.username = user.c_str();
|
||||
config.password = password.c_str();
|
||||
break;
|
||||
case INFLUXDB_V2:
|
||||
break;
|
||||
switch (version)
|
||||
{
|
||||
case INFLUXDB_V1:
|
||||
config.auth_type = HTTP_AUTH_TYPE_BASIC;
|
||||
config.username = user.c_str();
|
||||
config.password = password.c_str();
|
||||
break;
|
||||
case INFLUXDB_V2:
|
||||
break;
|
||||
}
|
||||
|
||||
InfluxDBdestroy();
|
||||
httpClient = esp_http_client_init(&config);
|
||||
if (!httpClient) {
|
||||
if (!httpClient)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to initialize HTTP client");
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "HTTP client initialized successfully");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Destroys the InfluxDB instance by cleaning up the HTTP client.
|
||||
*
|
||||
* This function checks if the HTTP client is initialized. If it is, it cleans up the HTTP client
|
||||
* and logs the cleanup action. The HTTP client pointer is then set to NULL.
|
||||
*/
|
||||
void InfluxDB::InfluxDBdestroy() {
|
||||
if (httpClient) {
|
||||
void InfluxDB::InfluxDBdestroy()
|
||||
{
|
||||
if (httpClient)
|
||||
{
|
||||
esp_http_client_cleanup(httpClient);
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "HTTP client cleaned up");
|
||||
httpClient = NULL;
|
||||
@@ -179,20 +183,20 @@ void InfluxDB::InfluxDBdestroy() {
|
||||
* It constructs the appropriate API URI based on the InfluxDB version and sends the data
|
||||
* using an HTTP POST request.
|
||||
*/
|
||||
void InfluxDB::InfluxDBPublish(std::string _measurement, std::string _key, std::string _content, long int _timeUTC) {
|
||||
void InfluxDB::InfluxDBPublish(std::string _measurement, std::string _key, std::string _content, long int _timeUTC)
|
||||
{
|
||||
std::string apiURI;
|
||||
std::string payload;
|
||||
char nowTimestamp[21];
|
||||
|
||||
connectHTTP();
|
||||
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "InfluxDBPublish - Key: " + _key + ", Content: " + _content + ", timeUTC: " + std::to_string(_timeUTC));
|
||||
|
||||
if (_timeUTC > 0)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Timestamp (UTC): " + std::to_string(_timeUTC));
|
||||
sprintf(nowTimestamp,"%ld000000000", _timeUTC); // UTC
|
||||
sprintf(nowTimestamp, "%ld000000000", _timeUTC); // UTC
|
||||
payload = _measurement + " " + _key + "=" + _content + " " + nowTimestamp;
|
||||
}
|
||||
else
|
||||
@@ -206,43 +210,48 @@ void InfluxDB::InfluxDBPublish(std::string _measurement, std::string _key, std::
|
||||
|
||||
esp_err_t err;
|
||||
|
||||
switch (version) {
|
||||
case INFLUXDB_V1:
|
||||
apiURI = influxDBURI + "/write?db=" + database;
|
||||
apiURI.shrink_to_fit();
|
||||
switch (version)
|
||||
{
|
||||
case INFLUXDB_V1:
|
||||
apiURI = influxDBURI + "/write?db=" + database;
|
||||
apiURI.shrink_to_fit();
|
||||
|
||||
esp_http_client_set_url(httpClient, apiURI.c_str());
|
||||
esp_http_client_set_method(httpClient, HTTP_METHOD_POST);
|
||||
esp_http_client_set_header(httpClient, "Content-Type", "text/plain");
|
||||
esp_http_client_set_post_field(httpClient, payload.c_str(), payload.length());
|
||||
esp_http_client_set_url(httpClient, apiURI.c_str());
|
||||
esp_http_client_set_method(httpClient, HTTP_METHOD_POST);
|
||||
esp_http_client_set_header(httpClient, "Content-Type", "text/plain");
|
||||
esp_http_client_set_post_field(httpClient, payload.c_str(), payload.length());
|
||||
|
||||
err = esp_http_client_perform(httpClient);
|
||||
if (err == ESP_OK) {
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Data published successfully: " + payload);
|
||||
} else {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to publish data: " + std::string(esp_err_to_name(err)));
|
||||
}
|
||||
break;
|
||||
err = esp_http_client_perform(httpClient);
|
||||
if (err == ESP_OK)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Data published successfully: " + payload);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to publish data: " + std::string(esp_err_to_name(err)));
|
||||
}
|
||||
break;
|
||||
|
||||
case INFLUXDB_V2:
|
||||
apiURI = influxDBURI + "/api/v2/write?org=" + org + "&bucket=" + bucket;
|
||||
apiURI.shrink_to_fit();
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "apiURI: " + apiURI);
|
||||
case INFLUXDB_V2:
|
||||
apiURI = influxDBURI + "/api/v2/write?org=" + org + "&bucket=" + bucket;
|
||||
apiURI.shrink_to_fit();
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "apiURI: " + apiURI);
|
||||
|
||||
esp_http_client_set_url(httpClient, apiURI.c_str());
|
||||
esp_http_client_set_method(httpClient, HTTP_METHOD_POST);
|
||||
esp_http_client_set_header(httpClient, "Content-Type", "text/plain");
|
||||
std::string _zw = "Token " + token;
|
||||
esp_http_client_set_header(httpClient, "Authorization", _zw.c_str());
|
||||
esp_http_client_set_post_field(httpClient, payload.c_str(), payload.length());
|
||||
err = ESP_ERROR_CHECK_WITHOUT_ABORT(esp_http_client_perform(httpClient));
|
||||
if (err == ESP_OK) {
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Data published successfully: " + payload);
|
||||
} else {
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Failed to publish data: " + std::string(esp_err_to_name(err)));
|
||||
}
|
||||
esp_http_client_set_url(httpClient, apiURI.c_str());
|
||||
esp_http_client_set_method(httpClient, HTTP_METHOD_POST);
|
||||
esp_http_client_set_header(httpClient, "Content-Type", "text/plain");
|
||||
std::string _zw = "Token " + token;
|
||||
esp_http_client_set_header(httpClient, "Authorization", _zw.c_str());
|
||||
esp_http_client_set_post_field(httpClient, payload.c_str(), payload.length());
|
||||
err = ESP_ERROR_CHECK_WITHOUT_ABORT(esp_http_client_perform(httpClient));
|
||||
if (err == ESP_OK)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Data published successfully: " + payload);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Failed to publish data: " + std::string(esp_err_to_name(err)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endif //ENABLE_INFLUXDB
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#ifdef ENABLE_INFLUXDB
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef INTERFACE_INFLUXDB_H
|
||||
#define INTERFACE_INFLUXDB_H
|
||||
|
||||
@@ -8,13 +7,12 @@
|
||||
#include <map>
|
||||
#include <functional>
|
||||
|
||||
|
||||
#include <string>
|
||||
#include "esp_http_client.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
|
||||
enum InfluxDBVersion {
|
||||
enum InfluxDBVersion
|
||||
{
|
||||
INFLUXDB_V1,
|
||||
INFLUXDB_V2
|
||||
};
|
||||
@@ -76,7 +74,8 @@ enum InfluxDBVersion {
|
||||
* @param _timeUTC The timestamp in UTC for the data point.
|
||||
*/
|
||||
|
||||
class InfluxDB {
|
||||
class InfluxDB
|
||||
{
|
||||
private:
|
||||
// Information for InfluxDB v1.x
|
||||
std::string influxDBURI = "";
|
||||
@@ -107,7 +106,4 @@ public:
|
||||
void InfluxDBPublish(std::string _measurement, std::string _key, std::string _content, long int _timeUTC);
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif //INTERFACE_INFLUXDB_H
|
||||
#endif //ENABLE_INFLUXDB
|
||||
#endif // INTERFACE_INFLUXDB_H
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
|
||||
|
||||
idf_component_register(SRCS ${app_sources}
|
||||
INCLUDE_DIRS "."
|
||||
INCLUDE_DIRS "." "../../include"
|
||||
REQUIRES jomjol_time_sntp jomjol_helper)
|
||||
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#include "defines.h"
|
||||
|
||||
#include "ClassLogFile.h"
|
||||
#include "time_sntp.h"
|
||||
#include "esp_log.h"
|
||||
@@ -7,7 +9,8 @@
|
||||
#include <algorithm>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
#include <dirent.h>
|
||||
#ifdef __cplusplus
|
||||
@@ -16,27 +19,25 @@ extern "C" {
|
||||
|
||||
#include "Helper.h"
|
||||
#include "time_sntp.h"
|
||||
#include "../../include/defines.h"
|
||||
|
||||
static const char *TAG = "LOGFILE";
|
||||
|
||||
ClassLogFile LogFile("/sdcard/log/message", "log_%Y-%m-%d.txt", "/sdcard/log/data", "data_%Y-%m-%d.csv");
|
||||
|
||||
|
||||
void ClassLogFile::WriteHeapInfo(std::string _id)
|
||||
{
|
||||
if (loglevel >= ESP_LOG_DEBUG) {
|
||||
std::string _zw = _id + "\t" + getESPHeapInfo();
|
||||
if (loglevel >= ESP_LOG_DEBUG)
|
||||
{
|
||||
std::string _zw = _id + "\t" + get_heapinfo();
|
||||
WriteToFile(ESP_LOG_DEBUG, "HEAP", _zw);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ClassLogFile::WriteToData(std::string _timestamp, std::string _name, std::string _ReturnRawValue, std::string _ReturnValue, std::string _ReturnPreValue, std::string _ReturnRateValue, std::string _ReturnChangeAbsolute, std::string _ErrorMessageText, std::string _digit, std::string _analog)
|
||||
void ClassLogFile::WriteToData(std::string _timestamp, std::string _name, std::string _ReturnRawValue, std::string _ReturnValue, std::string _ReturnPreValue, std::string _ReturnRateValue, std::string _ReturnChangeAbsolute, std::string _ErrorMessageText, std::string _digit, std::string _analog)
|
||||
{
|
||||
ESP_LOGD(TAG, "Start WriteToData");
|
||||
time_t rawtime;
|
||||
struct tm* timeinfo;
|
||||
struct tm *timeinfo;
|
||||
char buffer[30];
|
||||
|
||||
time(&rawtime);
|
||||
@@ -45,13 +46,14 @@ void ClassLogFile::WriteToData(std::string _timestamp, std::string _name, std::s
|
||||
strftime(buffer, 30, datafile.c_str(), timeinfo);
|
||||
std::string logpath = dataroot + "/" + buffer;
|
||||
|
||||
FILE* pFile;
|
||||
FILE *pFile;
|
||||
std::string zwtime;
|
||||
|
||||
ESP_LOGD(TAG, "Datalogfile: %s", logpath.c_str());
|
||||
pFile = fopen(logpath.c_str(), "a+");
|
||||
|
||||
if (pFile!=NULL) {
|
||||
if (pFile != NULL)
|
||||
{
|
||||
fputs(_timestamp.c_str(), pFile);
|
||||
fputs(",", pFile);
|
||||
fputs(_name.c_str(), pFile);
|
||||
@@ -72,35 +74,36 @@ void ClassLogFile::WriteToData(std::string _timestamp, std::string _name, std::s
|
||||
fputs("\n", pFile);
|
||||
|
||||
fclose(pFile);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGE(TAG, "Can't open data file %s", logpath.c_str());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void ClassLogFile::setLogLevel(esp_log_level_t _logLevel)
|
||||
{
|
||||
std::string levelText;
|
||||
|
||||
// Print log level to log file
|
||||
switch(_logLevel) {
|
||||
case ESP_LOG_WARN:
|
||||
levelText = "WARNING";
|
||||
break;
|
||||
switch (_logLevel)
|
||||
{
|
||||
case ESP_LOG_WARN:
|
||||
levelText = "WARNING";
|
||||
break;
|
||||
|
||||
case ESP_LOG_INFO:
|
||||
levelText = "INFO";
|
||||
break;
|
||||
case ESP_LOG_INFO:
|
||||
levelText = "INFO";
|
||||
break;
|
||||
|
||||
case ESP_LOG_DEBUG:
|
||||
levelText = "DEBUG";
|
||||
break;
|
||||
case ESP_LOG_DEBUG:
|
||||
levelText = "DEBUG";
|
||||
break;
|
||||
|
||||
case ESP_LOG_ERROR:
|
||||
default:
|
||||
levelText = "ERROR";
|
||||
break;
|
||||
case ESP_LOG_ERROR:
|
||||
default:
|
||||
levelText = "ERROR";
|
||||
break;
|
||||
}
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Set log level to " + levelText);
|
||||
|
||||
@@ -115,40 +118,38 @@ void ClassLogFile::setLogLevel(esp_log_level_t _logLevel)
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
void ClassLogFile::SetLogFileRetention(unsigned short _LogFileRetentionInDays){
|
||||
void ClassLogFile::SetLogFileRetention(unsigned short _LogFileRetentionInDays)
|
||||
{
|
||||
logFileRetentionInDays = _LogFileRetentionInDays;
|
||||
}
|
||||
|
||||
|
||||
void ClassLogFile::SetDataLogRetention(unsigned short _DataLogRetentionInDays){
|
||||
void ClassLogFile::SetDataLogRetention(unsigned short _DataLogRetentionInDays)
|
||||
{
|
||||
dataLogRetentionInDays = _DataLogRetentionInDays;
|
||||
}
|
||||
|
||||
|
||||
void ClassLogFile::SetDataLogToSD(bool _doDataLogToSD){
|
||||
void ClassLogFile::SetDataLogToSD(bool _doDataLogToSD)
|
||||
{
|
||||
doDataLogToSD = _doDataLogToSD;
|
||||
}
|
||||
|
||||
|
||||
bool ClassLogFile::GetDataLogToSD(){
|
||||
bool ClassLogFile::GetDataLogToSD()
|
||||
{
|
||||
return doDataLogToSD;
|
||||
}
|
||||
|
||||
|
||||
static FILE* logFileAppendHandle = NULL;
|
||||
static FILE *logFileAppendHandle = NULL;
|
||||
std::string fileNameDate;
|
||||
|
||||
void ClassLogFile::WriteToFile(esp_log_level_t level, std::string tag, std::string message, bool _time)
|
||||
{
|
||||
time_t rawtime;
|
||||
struct tm* timeinfo;
|
||||
struct tm *timeinfo;
|
||||
std::string fileNameDateNew;
|
||||
|
||||
std::string zwtime;
|
||||
std::string ntpTime = "";
|
||||
|
||||
|
||||
time(&rawtime);
|
||||
timeinfo = localtime(&rawtime);
|
||||
char buf[30];
|
||||
@@ -157,20 +158,21 @@ void ClassLogFile::WriteToFile(esp_log_level_t level, std::string tag, std::stri
|
||||
|
||||
std::replace(message.begin(), message.end(), '\n', ' '); // Replace all newline characters
|
||||
|
||||
if (tag != "") {
|
||||
if (tag != "")
|
||||
{
|
||||
ESP_LOG_LEVEL(level, tag.c_str(), "%s", message.c_str());
|
||||
message = "[" + tag + "] " + message;
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
ESP_LOG_LEVEL(level, "", "%s", message.c_str());
|
||||
}
|
||||
|
||||
|
||||
if (level > loglevel) {// Only write to file if loglevel is below threshold
|
||||
if (level > loglevel)
|
||||
{ // Only write to file if loglevel is below threshold
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (_time)
|
||||
{
|
||||
char logLineDate[30];
|
||||
@@ -179,42 +181,44 @@ void ClassLogFile::WriteToFile(esp_log_level_t level, std::string tag, std::stri
|
||||
}
|
||||
|
||||
std::string loglevelString;
|
||||
switch(level) {
|
||||
case ESP_LOG_ERROR:
|
||||
loglevelString = "ERR";
|
||||
break;
|
||||
case ESP_LOG_WARN:
|
||||
loglevelString = "WRN";
|
||||
break;
|
||||
case ESP_LOG_INFO:
|
||||
loglevelString = "INF";
|
||||
break;
|
||||
case ESP_LOG_DEBUG:
|
||||
loglevelString = "DBG";
|
||||
break;
|
||||
case ESP_LOG_VERBOSE:
|
||||
loglevelString = "VER";
|
||||
break;
|
||||
case ESP_LOG_NONE:
|
||||
default:
|
||||
loglevelString = "NONE";
|
||||
break;
|
||||
switch (level)
|
||||
{
|
||||
case ESP_LOG_ERROR:
|
||||
loglevelString = "ERR";
|
||||
break;
|
||||
case ESP_LOG_WARN:
|
||||
loglevelString = "WRN";
|
||||
break;
|
||||
case ESP_LOG_INFO:
|
||||
loglevelString = "INF";
|
||||
break;
|
||||
case ESP_LOG_DEBUG:
|
||||
loglevelString = "DBG";
|
||||
break;
|
||||
case ESP_LOG_VERBOSE:
|
||||
loglevelString = "VER";
|
||||
break;
|
||||
case ESP_LOG_NONE:
|
||||
default:
|
||||
loglevelString = "NONE";
|
||||
break;
|
||||
}
|
||||
|
||||
std::string formatedUptime = getFormatedUptime(true);
|
||||
|
||||
std::string fullmessage = "[" + formatedUptime + "] " + ntpTime + "\t<" + loglevelString + ">\t" + message + "\n";
|
||||
std::string formatedUptime = get_formated_uptime(true);
|
||||
|
||||
std::string fullmessage = "[" + formatedUptime + "] " + ntpTime + "\t<" + loglevelString + ">\t" + message + "\n";
|
||||
|
||||
#ifdef KEEP_LOGFILE_OPEN_FOR_APPENDING
|
||||
if (fileNameDateNew != fileNameDate) { // Filename changed
|
||||
if (fileNameDateNew != fileNameDate)
|
||||
{ // Filename changed
|
||||
// Make sure each day gets its own logfile
|
||||
// Also we need to re-open it in case it needed to get closed for reading
|
||||
std::string logpath = logroot + "/" + fileNameDateNew;
|
||||
|
||||
ESP_LOGI(TAG, "Opening logfile %s for appending", logpath.c_str());
|
||||
logFileAppendHandle = fopen(logpath.c_str(), "a+");
|
||||
if (logFileAppendHandle==NULL) {
|
||||
if (logFileAppendHandle == NULL)
|
||||
{
|
||||
ESP_LOGE(TAG, "Can't open log file %s", logpath.c_str());
|
||||
return;
|
||||
}
|
||||
@@ -224,11 +228,12 @@ void ClassLogFile::WriteToFile(esp_log_level_t level, std::string tag, std::stri
|
||||
#else
|
||||
std::string logpath = logroot + "/" + fileNameDateNew;
|
||||
logFileAppendHandle = fopen(logpath.c_str(), "a+");
|
||||
if (logFileAppendHandle==NULL) {
|
||||
if (logFileAppendHandle == NULL)
|
||||
{
|
||||
ESP_LOGE(TAG, "Can't open log file %s", logpath.c_str());
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
fputs(fullmessage.c_str(), logFileAppendHandle);
|
||||
|
||||
@@ -240,25 +245,25 @@ void ClassLogFile::WriteToFile(esp_log_level_t level, std::string tag, std::stri
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void ClassLogFile::CloseLogFileAppendHandle() {
|
||||
if (logFileAppendHandle != NULL) {
|
||||
void ClassLogFile::CloseLogFileAppendHandle()
|
||||
{
|
||||
if (logFileAppendHandle != NULL)
|
||||
{
|
||||
fclose(logFileAppendHandle);
|
||||
logFileAppendHandle = NULL;
|
||||
fileNameDate = "";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ClassLogFile::WriteToFile(esp_log_level_t level, std::string tag, std::string message) {
|
||||
void ClassLogFile::WriteToFile(esp_log_level_t level, std::string tag, std::string message)
|
||||
{
|
||||
LogFile.WriteToFile(level, tag, message, true);
|
||||
}
|
||||
|
||||
|
||||
std::string ClassLogFile::GetCurrentFileNameData()
|
||||
{
|
||||
time_t rawtime;
|
||||
struct tm* timeinfo;
|
||||
struct tm *timeinfo;
|
||||
char buffer[60];
|
||||
|
||||
time(&rawtime);
|
||||
@@ -270,11 +275,10 @@ std::string ClassLogFile::GetCurrentFileNameData()
|
||||
return logpath;
|
||||
}
|
||||
|
||||
|
||||
std::string ClassLogFile::GetCurrentFileName()
|
||||
{
|
||||
time_t rawtime;
|
||||
struct tm* timeinfo;
|
||||
struct tm *timeinfo;
|
||||
char buffer[60];
|
||||
|
||||
time(&rawtime);
|
||||
@@ -286,30 +290,30 @@ std::string ClassLogFile::GetCurrentFileName()
|
||||
return logpath;
|
||||
}
|
||||
|
||||
|
||||
void ClassLogFile::RemoveOldLogFile()
|
||||
{
|
||||
if (logFileRetentionInDays == 0) {
|
||||
if (logFileRetentionInDays == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "Remove old log files");
|
||||
|
||||
time_t rawtime;
|
||||
struct tm* timeinfo;
|
||||
struct tm *timeinfo;
|
||||
char cmpfilename[30];
|
||||
|
||||
time(&rawtime);
|
||||
rawtime = addDays(rawtime, -logFileRetentionInDays + 1);
|
||||
rawtime = add_days(rawtime, -logFileRetentionInDays + 1);
|
||||
timeinfo = localtime(&rawtime);
|
||||
//ESP_LOGD(TAG, "logFileRetentionInDays: %d", logFileRetentionInDays);
|
||||
|
||||
// ESP_LOGD(TAG, "logFileRetentionInDays: %d", logFileRetentionInDays);
|
||||
|
||||
strftime(cmpfilename, 30, logfile.c_str(), timeinfo);
|
||||
//ESP_LOGD(TAG, "log file name to compare: %s", cmpfilename);
|
||||
// ESP_LOGD(TAG, "log file name to compare: %s", cmpfilename);
|
||||
|
||||
DIR *dir = opendir(logroot.c_str());
|
||||
if (!dir) {
|
||||
if (!dir)
|
||||
{
|
||||
ESP_LOGE(TAG, "Failed to stat dir: %s", logroot.c_str());
|
||||
return;
|
||||
}
|
||||
@@ -317,27 +321,35 @@ void ClassLogFile::RemoveOldLogFile()
|
||||
struct dirent *entry;
|
||||
int deleted = 0;
|
||||
int notDeleted = 0;
|
||||
while ((entry = readdir(dir)) != NULL) {
|
||||
if (entry->d_type == DT_REG) {
|
||||
//ESP_LOGD(TAG, "compare log file: %s to %s", entry->d_name, cmpfilename);
|
||||
if ((strlen(entry->d_name) == strlen(cmpfilename)) && (strcmp(entry->d_name, cmpfilename) < 0)) {
|
||||
//ESP_LOGD(TAG, "delete log file: %s", entry->d_name);
|
||||
while ((entry = readdir(dir)) != NULL)
|
||||
{
|
||||
if (entry->d_type == DT_REG)
|
||||
{
|
||||
// ESP_LOGD(TAG, "compare log file: %s to %s", entry->d_name, cmpfilename);
|
||||
if ((strlen(entry->d_name) == strlen(cmpfilename)) && (strcmp(entry->d_name, cmpfilename) < 0))
|
||||
{
|
||||
// ESP_LOGD(TAG, "delete log file: %s", entry->d_name);
|
||||
std::string filepath = logroot + "/" + entry->d_name;
|
||||
if ((strcmp(entry->d_name, "log_1970-01-01.txt") == 0) && getTimeWasNotSetAtBoot()) { // keep logfile log_1970-01-01.txt if time was not set at boot (some boot logs are in there)
|
||||
//ESP_LOGD(TAG, "Skip deleting this file: %s", entry->d_name);
|
||||
if ((strcmp(entry->d_name, "log_1970-01-01.txt") == 0) && getTimeWasNotSetAtBoot())
|
||||
{ // keep logfile log_1970-01-01.txt if time was not set at boot (some boot logs are in there)
|
||||
// ESP_LOGD(TAG, "Skip deleting this file: %s", entry->d_name);
|
||||
notDeleted++;
|
||||
}
|
||||
else {
|
||||
if (unlink(filepath.c_str()) == 0) {
|
||||
else
|
||||
{
|
||||
if (unlink(filepath.c_str()) == 0)
|
||||
{
|
||||
deleted++;
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
ESP_LOGE(TAG, "can't delete file: %s", entry->d_name);
|
||||
notDeleted++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
notDeleted++;
|
||||
}
|
||||
}
|
||||
@@ -346,29 +358,30 @@ void ClassLogFile::RemoveOldLogFile()
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
|
||||
void ClassLogFile::RemoveOldDataLog()
|
||||
{
|
||||
if (dataLogRetentionInDays == 0 || !doDataLogToSD) {
|
||||
if (dataLogRetentionInDays == 0 || !doDataLogToSD)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "Remove old data files");
|
||||
|
||||
time_t rawtime;
|
||||
struct tm* timeinfo;
|
||||
struct tm *timeinfo;
|
||||
char cmpfilename[30];
|
||||
|
||||
time(&rawtime);
|
||||
rawtime = addDays(rawtime, -dataLogRetentionInDays + 1);
|
||||
rawtime = add_days(rawtime, -dataLogRetentionInDays + 1);
|
||||
timeinfo = localtime(&rawtime);
|
||||
//ESP_LOGD(TAG, "dataLogRetentionInDays: %d", dataLogRetentionInDays);
|
||||
// ESP_LOGD(TAG, "dataLogRetentionInDays: %d", dataLogRetentionInDays);
|
||||
|
||||
strftime(cmpfilename, 30, datafile.c_str(), timeinfo);
|
||||
//ESP_LOGD(TAG, "data file name to compare: %s", cmpfilename);
|
||||
// ESP_LOGD(TAG, "data file name to compare: %s", cmpfilename);
|
||||
|
||||
DIR *dir = opendir(dataroot.c_str());
|
||||
if (!dir) {
|
||||
if (!dir)
|
||||
{
|
||||
ESP_LOGE(TAG, "Failed to stat dir: %s", dataroot.c_str());
|
||||
return;
|
||||
}
|
||||
@@ -376,20 +389,28 @@ void ClassLogFile::RemoveOldDataLog()
|
||||
struct dirent *entry;
|
||||
int deleted = 0;
|
||||
int notDeleted = 0;
|
||||
while ((entry = readdir(dir)) != NULL) {
|
||||
if (entry->d_type == DT_REG) {
|
||||
//ESP_LOGD(TAG, "Compare data file: %s to %s", entry->d_name, cmpfilename);
|
||||
if ((strlen(entry->d_name) == strlen(cmpfilename)) && (strcmp(entry->d_name, cmpfilename) < 0)) {
|
||||
//ESP_LOGD(TAG, "delete data file: %s", entry->d_name);
|
||||
while ((entry = readdir(dir)) != NULL)
|
||||
{
|
||||
if (entry->d_type == DT_REG)
|
||||
{
|
||||
// ESP_LOGD(TAG, "Compare data file: %s to %s", entry->d_name, cmpfilename);
|
||||
if ((strlen(entry->d_name) == strlen(cmpfilename)) && (strcmp(entry->d_name, cmpfilename) < 0))
|
||||
{
|
||||
// ESP_LOGD(TAG, "delete data file: %s", entry->d_name);
|
||||
std::string filepath = dataroot + "/" + entry->d_name;
|
||||
if (unlink(filepath.c_str()) == 0) {
|
||||
deleted ++;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "can't delete file: %s", entry->d_name);
|
||||
notDeleted ++;
|
||||
if (unlink(filepath.c_str()) == 0)
|
||||
{
|
||||
deleted++;
|
||||
}
|
||||
} else {
|
||||
notDeleted ++;
|
||||
else
|
||||
{
|
||||
ESP_LOGE(TAG, "can't delete file: %s", entry->d_name);
|
||||
notDeleted++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
notDeleted++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -397,25 +418,23 @@ void ClassLogFile::RemoveOldDataLog()
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
|
||||
bool ClassLogFile::CreateLogDirectories()
|
||||
{
|
||||
bool bRetval = false;
|
||||
bRetval = MakeDir("/sdcard/log");
|
||||
bRetval = MakeDir("/sdcard/log/data");
|
||||
bRetval = MakeDir("/sdcard/log/analog");
|
||||
bRetval = MakeDir("/sdcard/log/digit");
|
||||
bRetval = MakeDir("/sdcard/log/message");
|
||||
bRetval = MakeDir("/sdcard/log/source");
|
||||
bRetval = make_dir("/sdcard/log");
|
||||
bRetval = make_dir("/sdcard/log/data");
|
||||
bRetval = make_dir("/sdcard/log/analog");
|
||||
bRetval = make_dir("/sdcard/log/digit");
|
||||
bRetval = make_dir("/sdcard/log/message");
|
||||
bRetval = make_dir("/sdcard/log/source");
|
||||
|
||||
return bRetval;
|
||||
}
|
||||
|
||||
|
||||
ClassLogFile::ClassLogFile(std::string _logroot, std::string _logfile, std::string _logdatapath, std::string _datafile)
|
||||
{
|
||||
logroot = _logroot;
|
||||
logfile = _logfile;
|
||||
logfile = _logfile;
|
||||
datafile = _datafile;
|
||||
dataroot = _logdatapath;
|
||||
logFileRetentionInDays = 3;
|
||||
|
||||
@@ -3,11 +3,9 @@
|
||||
#ifndef CLASSLOGFILE_H
|
||||
#define CLASSLOGFILE_H
|
||||
|
||||
|
||||
#include <string>
|
||||
#include "esp_log.h"
|
||||
|
||||
|
||||
class ClassLogFile
|
||||
{
|
||||
private:
|
||||
@@ -19,6 +17,7 @@ private:
|
||||
unsigned short dataLogRetentionInDays;
|
||||
bool doDataLogToSD;
|
||||
esp_log_level_t loglevel;
|
||||
|
||||
public:
|
||||
ClassLogFile(std::string _logpath, std::string _logfile, std::string _logdatapath, std::string _datafile);
|
||||
|
||||
@@ -39,9 +38,8 @@ public:
|
||||
void RemoveOldLogFile();
|
||||
void RemoveOldDataLog();
|
||||
|
||||
// void WriteToData(std::string _ReturnRawValue, std::string _ReturnValue, std::string _ReturnPreValue, std::string _ErrorMessageText, std::string _digit, std::string _analog);
|
||||
void WriteToData(std::string _timestamp, std::string _name, std::string _ReturnRawValue, std::string _ReturnValue, std::string _ReturnPreValue, std::string _ReturnRateValue, std::string _ReturnChangeAbsolute, std::string _ErrorMessageText, std::string _digit, std::string _analog);
|
||||
|
||||
// void WriteToData(std::string _ReturnRawValue, std::string _ReturnValue, std::string _ReturnPreValue, std::string _ErrorMessageText, std::string _digit, std::string _analog);
|
||||
void WriteToData(std::string _timestamp, std::string _name, std::string _ReturnRawValue, std::string _ReturnValue, std::string _ReturnPreValue, std::string _ReturnRateValue, std::string _ReturnChangeAbsolute, std::string _ErrorMessageText, std::string _digit, std::string _analog);
|
||||
|
||||
std::string GetCurrentFileName();
|
||||
std::string GetCurrentFileNameData();
|
||||
@@ -49,4 +47,4 @@ public:
|
||||
|
||||
extern ClassLogFile LogFile;
|
||||
|
||||
#endif //CLASSLOGFILE_H
|
||||
#endif // CLASSLOGFILE_H
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
|
||||
|
||||
idf_component_register(SRCS ${app_sources}
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES esp_timer esp-tflite-micro mqtt jomjol_tfliteclass jomjol_helper jomjol_mqtt jomjol_wlan json)
|
||||
INCLUDE_DIRS "." "../../include"
|
||||
REQUIRES esp_timer esp-tflite-micro mqtt jomjol_tfliteclass jomjol_helper jomjol_mqtt jomjol_network json)
|
||||
|
||||
@@ -1,86 +1,71 @@
|
||||
#ifdef ENABLE_MQTT
|
||||
#include "defines.h"
|
||||
|
||||
#include "interface_mqtt.h"
|
||||
|
||||
#include "esp_log.h"
|
||||
#if DEBUG_DETAIL_ON
|
||||
#include "esp_timer.h"
|
||||
#endif
|
||||
#include "connect_wlan.h"
|
||||
#include "mqtt_client.h"
|
||||
#include <esp_log.h>
|
||||
#include <esp_timer.h>
|
||||
#include <mqtt_client.h>
|
||||
#include <cJSON.h>
|
||||
|
||||
#include "Helper.h"
|
||||
|
||||
#include "connect_wifi_sta.h"
|
||||
#include "read_network_config.h"
|
||||
|
||||
#include "ClassLogFile.h"
|
||||
#include "MainFlowControl.h"
|
||||
#include "cJSON.h"
|
||||
#include "../../include/defines.h"
|
||||
|
||||
#if DEBUG_DETAIL_ON
|
||||
#include "esp_timer.h"
|
||||
#endif
|
||||
|
||||
static const char *TAG = "MQTT IF";
|
||||
|
||||
std::map<std::string, std::function<void()>>* connectFunktionMap = NULL;
|
||||
std::map<std::string, std::function<bool(std::string, char*, int)>>* subscribeFunktionMap = NULL;
|
||||
std::map<std::string, std::function<void()>> *connectFunktionMap = NULL;
|
||||
std::map<std::string, std::function<bool(std::string, char *, int)>> *subscribeFunktionMap = NULL;
|
||||
|
||||
esp_mqtt_client_handle_t client = NULL;
|
||||
|
||||
std::string caCert = "";
|
||||
std::string clientCert = "";
|
||||
std::string clientKey = "";
|
||||
|
||||
int failedOnRound = -1;
|
||||
int MQTTReconnectCnt = 0;
|
||||
|
||||
esp_mqtt_event_id_t esp_mqtt_ID = MQTT_EVENT_ANY;
|
||||
// ESP_EVENT_ANY_ID
|
||||
|
||||
bool mqtt_enabled = false;
|
||||
bool mqtt_configOK = false;
|
||||
bool mqtt_initialized = false;
|
||||
bool mqtt_connected = false;
|
||||
|
||||
esp_mqtt_client_handle_t client = NULL;
|
||||
std::string uri, client_id, lwt_topic, lwt_connected, lwt_disconnected, user, password, maintopic, domoticz_in_topic;
|
||||
std::string caCert, clientCert, clientKey;
|
||||
bool validateServerCert = true;
|
||||
int keepalive;
|
||||
bool SetRetainFlag;
|
||||
void (*callbackOnConnected)(std::string, bool) = NULL;
|
||||
|
||||
bool MQTTPublish(std::string _key, std::string _content, int qos, bool retained_flag)
|
||||
{
|
||||
if (!mqtt_enabled) { // MQTT sevice not started / configured (MQTT_Init not called before)
|
||||
if (!mqtt_controll_config.mqtt_enabled)
|
||||
{
|
||||
// MQTT sevice not started / configured (MQTT_Init not called before)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (failedOnRound == getCountFlowRounds()) { // we already failed in this round, do not retry until the next round
|
||||
if (failedOnRound == getCountFlowRounds())
|
||||
{
|
||||
// we already failed in this round, do not retry until the next round
|
||||
return true; // Fail quietly
|
||||
}
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("MQTT Publish");
|
||||
#endif
|
||||
|
||||
MQTT_Init(); // Re-Init client if not initialized yet/anymore
|
||||
|
||||
if (mqtt_initialized && mqtt_connected) {
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
long long int starttime = esp_timer_get_time();
|
||||
#endif
|
||||
if (mqtt_controll_config.mqtt_initialized && mqtt_controll_config.mqtt_connected)
|
||||
{
|
||||
int msg_id = esp_mqtt_client_publish(client, _key.c_str(), _content.c_str(), 0, qos, retained_flag);
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
ESP_LOGD(TAG, "Publish msg_id %d in %lld ms", msg_id, (esp_timer_get_time() - starttime)/1000);
|
||||
#endif
|
||||
if (msg_id == -1) {
|
||||
if (msg_id == -1)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Failed to publish topic '" + _key + "', re-trying...");
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
starttime = esp_timer_get_time();
|
||||
#endif
|
||||
msg_id = esp_mqtt_client_publish(client, _key.c_str(), _content.c_str(), 0, qos, retained_flag);
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
ESP_LOGD(TAG, "Publish msg_id %d in %lld ms", msg_id, (esp_timer_get_time() - starttime)/1000);
|
||||
#endif
|
||||
if (msg_id == -1) {
|
||||
|
||||
if (msg_id == -1)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Failed to publish topic '" + _key + "', skipping all MQTT publishings in this round!");
|
||||
failedOnRound = getCountFlowRounds();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (_content.length() > 80) { // Truncate message if too long
|
||||
if (_content.length() > 80)
|
||||
{
|
||||
// Truncate message if too long
|
||||
_content.resize(80);
|
||||
_content.append("..");
|
||||
}
|
||||
@@ -88,208 +73,216 @@ bool MQTTPublish(std::string _key, std::string _content, int qos, bool retained_
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Published topic: " + _key + ", content: " + _content);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Publish skipped. Client not initalized or not connected. (topic: " + _key + ")");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static esp_err_t mqtt_event_handler_cb(esp_mqtt_event_handle_t event) {
|
||||
static esp_err_t mqtt_event_handler_cb(esp_mqtt_event_handle_t event)
|
||||
{
|
||||
std::string topic = "";
|
||||
switch (event->event_id) {
|
||||
case MQTT_EVENT_BEFORE_CONNECT:
|
||||
mqtt_initialized = true;
|
||||
break;
|
||||
switch (event->event_id)
|
||||
{
|
||||
case MQTT_EVENT_BEFORE_CONNECT:
|
||||
mqtt_controll_config.mqtt_initialized = true;
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_CONNECTED:
|
||||
case MQTT_EVENT_CONNECTED:
|
||||
MQTTReconnectCnt = 0;
|
||||
mqtt_controll_config.mqtt_initialized = true;
|
||||
mqtt_controll_config.mqtt_connected = true;
|
||||
MQTTconnected();
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_DISCONNECTED:
|
||||
mqtt_controll_config.mqtt_connected = false;
|
||||
MQTTReconnectCnt++;
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Disconnected, trying to reconnect");
|
||||
|
||||
if (MQTTReconnectCnt >= 5)
|
||||
{
|
||||
MQTTReconnectCnt = 0;
|
||||
mqtt_initialized = true;
|
||||
mqtt_connected = true;
|
||||
MQTTconnected();
|
||||
break;
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Disconnected, multiple reconnect attempts failed, still retrying...");
|
||||
}
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_DISCONNECTED:
|
||||
mqtt_connected = false;
|
||||
MQTTReconnectCnt++;
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Disconnected, trying to reconnect");
|
||||
case MQTT_EVENT_SUBSCRIBED:
|
||||
ESP_LOGD(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);
|
||||
break;
|
||||
|
||||
if (MQTTReconnectCnt >= 5) {
|
||||
MQTTReconnectCnt = 0;
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Disconnected, multiple reconnect attempts failed, still retrying...");
|
||||
case MQTT_EVENT_UNSUBSCRIBED:
|
||||
ESP_LOGD(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_PUBLISHED:
|
||||
ESP_LOGD(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_DATA:
|
||||
ESP_LOGD(TAG, "MQTT_EVENT_DATA");
|
||||
ESP_LOGD(TAG, "TOPIC=%.*s", event->topic_len, event->topic);
|
||||
ESP_LOGD(TAG, "DATA=%.*s", event->data_len, event->data);
|
||||
topic.assign(event->topic, event->topic_len);
|
||||
if (subscribeFunktionMap != NULL)
|
||||
{
|
||||
if (subscribeFunktionMap->find(topic) != subscribeFunktionMap->end())
|
||||
{
|
||||
ESP_LOGD(TAG, "call subcribe function for topic %s", topic.c_str());
|
||||
(*subscribeFunktionMap)[topic](topic, event->data, event->data_len);
|
||||
}
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGW(TAG, "no handler available\r\n");
|
||||
}
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_SUBSCRIBED:
|
||||
ESP_LOGD(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);
|
||||
break;
|
||||
case MQTT_EVENT_ERROR:
|
||||
// http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718033 --> chapter 3.2.2.3
|
||||
|
||||
case MQTT_EVENT_UNSUBSCRIBED:
|
||||
ESP_LOGD(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
|
||||
break;
|
||||
// The server does not support the level of the MQTT protocol requested by the client
|
||||
// NOTE: Only protocol 3.1.1 is supported (refer to setting in sdkconfig)
|
||||
if (event->error_handle->connect_return_code == MQTT_CONNECTION_REFUSE_PROTOCOL)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Connection refused, unacceptable protocol version (0x01)");
|
||||
}
|
||||
// The client identifier is correct UTF-8 but not allowed by the server
|
||||
// e.g. clientID empty (cannot be the case -> default set in firmware)
|
||||
else if (event->error_handle->connect_return_code == MQTT_CONNECTION_REFUSE_ID_REJECTED)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Connection refused, identifier rejected (0x02)");
|
||||
}
|
||||
// The network connection has been made but the MQTT service is unavailable
|
||||
else if (event->error_handle->connect_return_code == MQTT_CONNECTION_REFUSE_SERVER_UNAVAILABLE)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Connection refused, Server unavailable (0x03)");
|
||||
}
|
||||
// The data in the user name or password is malformed
|
||||
else if (event->error_handle->connect_return_code == MQTT_CONNECTION_REFUSE_BAD_USERNAME)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Connection refused, malformed data in username or password (0x04)");
|
||||
}
|
||||
// The client is not authorized to connect
|
||||
else if (event->error_handle->connect_return_code == MQTT_CONNECTION_REFUSE_NOT_AUTHORIZED)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Connection refused, not authorized. Check username/password (0x05)");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Other event id:" + event->error_handle->connect_return_code);
|
||||
ESP_LOGE(TAG, "Other event id:%d", event->error_handle->connect_return_code);
|
||||
}
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_PUBLISHED:
|
||||
ESP_LOGD(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_DATA:
|
||||
ESP_LOGD(TAG, "MQTT_EVENT_DATA");
|
||||
ESP_LOGD(TAG, "TOPIC=%.*s", event->topic_len, event->topic);
|
||||
ESP_LOGD(TAG, "DATA=%.*s", event->data_len, event->data);
|
||||
topic.assign(event->topic, event->topic_len);
|
||||
if (subscribeFunktionMap != NULL) {
|
||||
if (subscribeFunktionMap->find(topic) != subscribeFunktionMap->end()) {
|
||||
ESP_LOGD(TAG, "call subcribe function for topic %s", topic.c_str());
|
||||
(*subscribeFunktionMap)[topic](topic, event->data, event->data_len);
|
||||
}
|
||||
} else {
|
||||
ESP_LOGW(TAG, "no handler available\r\n");
|
||||
}
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_ERROR:
|
||||
// http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718033 --> chapter 3.2.2.3
|
||||
|
||||
// The server does not support the level of the MQTT protocol requested by the client
|
||||
// NOTE: Only protocol 3.1.1 is supported (refer to setting in sdkconfig)
|
||||
if (event->error_handle->connect_return_code == MQTT_CONNECTION_REFUSE_PROTOCOL) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Connection refused, unacceptable protocol version (0x01)");
|
||||
}
|
||||
// The client identifier is correct UTF-8 but not allowed by the server
|
||||
// e.g. clientID empty (cannot be the case -> default set in firmware)
|
||||
else if (event->error_handle->connect_return_code == MQTT_CONNECTION_REFUSE_ID_REJECTED) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Connection refused, identifier rejected (0x02)");
|
||||
}
|
||||
// The network connection has been made but the MQTT service is unavailable
|
||||
else if (event->error_handle->connect_return_code == MQTT_CONNECTION_REFUSE_SERVER_UNAVAILABLE) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Connection refused, Server unavailable (0x03)");
|
||||
}
|
||||
// The data in the user name or password is malformed
|
||||
else if (event->error_handle->connect_return_code == MQTT_CONNECTION_REFUSE_BAD_USERNAME) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Connection refused, malformed data in username or password (0x04)");
|
||||
}
|
||||
// The client is not authorized to connect
|
||||
else if (event->error_handle->connect_return_code == MQTT_CONNECTION_REFUSE_NOT_AUTHORIZED) {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Connection refused, not authorized. Check username/password (0x05)");
|
||||
}
|
||||
else {
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Other event id:" + event->error_handle->connect_return_code);
|
||||
ESP_LOGE(TAG, "Other event id:%d", event->error_handle->connect_return_code);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
ESP_LOGD(TAG, "MQTT_EVENT_ERROR - esp_mqtt_error_codes:");
|
||||
ESP_LOGD(TAG, "error_type:%d", event->error_handle->error_type);
|
||||
ESP_LOGD(TAG, "connect_return_code:%d", event->error_handle->connect_return_code);
|
||||
ESP_LOGD(TAG, "esp_transport_sock_errno:%d", event->error_handle->esp_transport_sock_errno);
|
||||
ESP_LOGD(TAG, "esp_tls_last_esp_err:%d", event->error_handle->esp_tls_last_esp_err);
|
||||
ESP_LOGD(TAG, "esp_tls_stack_err:%d", event->error_handle->esp_tls_stack_err);
|
||||
ESP_LOGD(TAG, "esp_tls_cert_verify_flags:%d", event->error_handle->esp_tls_cert_verify_flags);
|
||||
#endif
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
ESP_LOGD(TAG, "Other event id:%d", event->event_id);
|
||||
break;
|
||||
default:
|
||||
ESP_LOGD(TAG, "Other event id:%d", event->event_id);
|
||||
break;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data) {
|
||||
static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
|
||||
{
|
||||
ESP_LOGD(TAG, "Event dispatched from event loop base=%s, event_id=%d", base, (int)event_id);
|
||||
mqtt_event_handler_cb((esp_mqtt_event_handle_t) event_data);
|
||||
mqtt_event_handler_cb((esp_mqtt_event_handle_t)event_data);
|
||||
}
|
||||
|
||||
bool MQTT_Configure(std::string _mqttURI, std::string _clientid, std::string _user, std::string _password,
|
||||
std::string _maintopic, std::string _domoticz_in_topic, std::string _lwt, std::string _lwt_connected, std::string _lwt_disconnected,
|
||||
std::string _cacertfilename, bool _validateServerCert, std::string _clientcertfilename, std::string _clientkeyfilename,
|
||||
int _keepalive, bool _SetRetainFlag, void *_callbackOnConnected) {
|
||||
if ((_mqttURI.length() == 0) || (_maintopic.length() == 0) || (_clientid.length() == 0))
|
||||
bool MQTT_Configure(void *_callbackOnConnected)
|
||||
{
|
||||
if ((mqtt_controll_config.uri.length() == 0) || (mqtt_controll_config.maintopic.length() == 0) || (mqtt_controll_config.clientname.length() == 0))
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Init aborted! Config error (URI, MainTopic or ClientID missing)");
|
||||
return false;
|
||||
}
|
||||
|
||||
uri = _mqttURI;
|
||||
client_id = _clientid;
|
||||
lwt_topic = _maintopic + "/" + _lwt;
|
||||
lwt_connected = _lwt_connected;
|
||||
lwt_disconnected = _lwt_disconnected;
|
||||
keepalive = _keepalive;
|
||||
SetRetainFlag = _SetRetainFlag;
|
||||
maintopic = _maintopic;
|
||||
domoticz_in_topic = _domoticz_in_topic;
|
||||
callbackOnConnected = ( void (*)(std::string, bool) )(_callbackOnConnected);
|
||||
callbackOnConnected = (void (*)(std::string, bool))(_callbackOnConnected);
|
||||
|
||||
if (_clientcertfilename.length() && _clientkeyfilename.length()) {
|
||||
std::ifstream cert_ifs(_clientcertfilename);
|
||||
if (cert_ifs.is_open()) {
|
||||
if (mqtt_controll_config.clientCertFilename.length() && mqtt_controll_config.clientKeyFilename.length())
|
||||
{
|
||||
std::ifstream cert_ifs(mqtt_controll_config.clientCertFilename);
|
||||
if (cert_ifs.is_open())
|
||||
{
|
||||
std::string cert_content((std::istreambuf_iterator<char>(cert_ifs)), (std::istreambuf_iterator<char>()));
|
||||
clientCert = cert_content;
|
||||
cert_ifs.close();
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "using clientCert: " + _clientcertfilename);
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "using clientCert: " + mqtt_controll_config.clientCertFilename);
|
||||
}
|
||||
else {
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "could not open clientCert: " + _clientcertfilename);
|
||||
else
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "could not open clientCert: " + mqtt_controll_config.clientCertFilename);
|
||||
}
|
||||
|
||||
std::ifstream key_ifs(_clientkeyfilename);
|
||||
if (key_ifs.is_open()) {
|
||||
std::ifstream key_ifs(mqtt_controll_config.clientKeyFilename);
|
||||
if (key_ifs.is_open())
|
||||
{
|
||||
std::string key_content((std::istreambuf_iterator<char>(key_ifs)), (std::istreambuf_iterator<char>()));
|
||||
clientKey = key_content;
|
||||
key_ifs.close();
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "using clientKey: " + _clientkeyfilename);
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "using clientKey: " + mqtt_controll_config.clientKeyFilename);
|
||||
}
|
||||
else {
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "could not open clientKey: " + _clientkeyfilename);
|
||||
else
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "could not open clientKey: " + mqtt_controll_config.clientKeyFilename);
|
||||
}
|
||||
}
|
||||
|
||||
if (_cacertfilename.length()) {
|
||||
std::ifstream ca_ifs(_cacertfilename);
|
||||
if (ca_ifs.is_open()) {
|
||||
if (mqtt_controll_config.caCertFilename.length())
|
||||
{
|
||||
std::ifstream ca_ifs(mqtt_controll_config.caCertFilename);
|
||||
if (ca_ifs.is_open())
|
||||
{
|
||||
std::string content((std::istreambuf_iterator<char>(ca_ifs)), (std::istreambuf_iterator<char>()));
|
||||
caCert = content;
|
||||
ca_ifs.close();
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "using caCert: " + _cacertfilename);
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "using caCert: " + mqtt_controll_config.caCertFilename);
|
||||
}
|
||||
else {
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "could not open caCert: " + _cacertfilename);
|
||||
else
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "could not open caCert: " + mqtt_controll_config.caCertFilename);
|
||||
}
|
||||
}
|
||||
|
||||
validateServerCert = _validateServerCert;
|
||||
#ifdef __HIDE_PASSWORD
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG,
|
||||
"URI: " + mqtt_controll_config.uri + ", clientname: " + mqtt_controll_config.clientname +
|
||||
", user: " + mqtt_controll_config.user + ", password: XXXXXXXX, maintopic: " + mqtt_controll_config.maintopic +
|
||||
", last-will-topic: " + mqtt_controll_config.lwt_topic +
|
||||
", keepAlive: " + std::to_string(mqtt_controll_config.keepAlive) +
|
||||
", RetainFlag: " + std::to_string(mqtt_controll_config.retainFlag));
|
||||
#else
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG,
|
||||
"URI: " + mqtt_controll_config.uri + ", clientname: " + mqtt_controll_config.clientname +
|
||||
", user: " + mqtt_controll_config.user + ", password: " + mqtt_controll_config.password +
|
||||
", maintopic: " + mqtt_controll_config.maintopic + ", last-will-topic: " + mqtt_controll_config.lwt_topic +
|
||||
", keepAlive: " + std::to_string(mqtt_controll_config.keepAlive) +
|
||||
", RetainFlag: " + std::to_string(mqtt_controll_config.retainFlag));
|
||||
#endif
|
||||
|
||||
if (_user.length() && _password.length()){
|
||||
user = _user;
|
||||
password = _password;
|
||||
}
|
||||
|
||||
#ifdef __HIDE_PASSWORD
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "URI: " + uri + ", clientname: " + client_id + ", user: " + user + ", password: XXXXXXXX, maintopic: "
|
||||
+ maintopic + ", last-will-topic: " + lwt_topic + ", keepAlive: " + std::to_string(keepalive) + ", RetainFlag: " + std::to_string(SetRetainFlag));
|
||||
#else
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "URI: " + uri + ", clientname: " + client_id + ", user: " + user + ", password: " + password + ", maintopic: "
|
||||
+ maintopic + ", last-will-topic: " + lwt_topic + ", keepAlive: " + std::to_string(keepalive) + ", RetainFlag: " + std::to_string(SetRetainFlag));
|
||||
#endif
|
||||
|
||||
mqtt_configOK = true;
|
||||
mqtt_controll_config.mqtt_configOK = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
int MQTT_Init() {
|
||||
if (mqtt_initialized) {
|
||||
int MQTT_Init()
|
||||
{
|
||||
if (mqtt_controll_config.mqtt_initialized)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (mqtt_configOK) {
|
||||
mqtt_enabled = true;
|
||||
} else {
|
||||
if (mqtt_controll_config.mqtt_configOK)
|
||||
{
|
||||
mqtt_controll_config.mqtt_enabled = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Init called, but client is not yet configured.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!getWIFIisConnected()) {
|
||||
if (!get_wifi_sta_is_connected())
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Init called, but WIFI is not yet connected.");
|
||||
return 0;
|
||||
}
|
||||
@@ -297,31 +290,33 @@ int MQTT_Init() {
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Init");
|
||||
MQTTdestroy_client(false);
|
||||
|
||||
esp_mqtt_client_config_t mqtt_cfg = { };
|
||||
esp_mqtt_client_config_t mqtt_cfg = {};
|
||||
|
||||
mqtt_cfg.broker.address.uri = uri.c_str();
|
||||
mqtt_cfg.credentials.client_id = client_id.c_str();
|
||||
mqtt_cfg.network.disable_auto_reconnect = false; // Reconnection routine active (Default: false)
|
||||
mqtt_cfg.network.reconnect_timeout_ms = 15000; // Try to reconnect to broker (Default: 10000ms)
|
||||
mqtt_cfg.network.timeout_ms = 20000; // Network Timeout (Default: 10000ms)
|
||||
mqtt_cfg.session.message_retransmit_timeout = 3000; // Time after message resent when broker not acknowledged (QoS1, QoS2)
|
||||
mqtt_cfg.session.last_will.topic = lwt_topic.c_str();
|
||||
mqtt_cfg.broker.address.uri = mqtt_controll_config.uri.c_str();
|
||||
mqtt_cfg.credentials.client_id = mqtt_controll_config.clientname.c_str();
|
||||
mqtt_cfg.network.disable_auto_reconnect = false; // Reconnection routine active (Default: false)
|
||||
mqtt_cfg.network.reconnect_timeout_ms = 15000; // Try to reconnect to broker (Default: 10000ms)
|
||||
mqtt_cfg.network.timeout_ms = 20000; // Network Timeout (Default: 10000ms)
|
||||
mqtt_cfg.session.message_retransmit_timeout = 3000; // Time after message resent when broker not acknowledged (QoS1, QoS2)
|
||||
mqtt_cfg.session.last_will.topic = mqtt_controll_config.lwt_topic.c_str();
|
||||
mqtt_cfg.session.last_will.retain = 1;
|
||||
mqtt_cfg.session.last_will.msg = lwt_disconnected.c_str();
|
||||
mqtt_cfg.session.last_will.msg_len = (int)(lwt_disconnected.length());
|
||||
mqtt_cfg.session.keepalive = keepalive;
|
||||
mqtt_cfg.buffer.size = 2048; // size of MQTT send/receive buffer
|
||||
mqtt_cfg.session.last_will.msg = mqtt_controll_config.lwt_disconnected.c_str();
|
||||
mqtt_cfg.session.last_will.msg_len = (int)(mqtt_controll_config.lwt_disconnected.length());
|
||||
mqtt_cfg.session.keepalive = mqtt_controll_config.keepAlive;
|
||||
mqtt_cfg.buffer.size = 2048; // size of MQTT send/receive buffer
|
||||
|
||||
if (caCert.length()) {
|
||||
if (caCert.length())
|
||||
{
|
||||
mqtt_cfg.broker.verification.certificate = caCert.c_str();
|
||||
mqtt_cfg.broker.verification.certificate_len = caCert.length() + 1;
|
||||
|
||||
// Skip any validation of server certificate CN field, this reduces the
|
||||
// security of TLS and makes the *MQTT* client susceptible to MITM attacks
|
||||
mqtt_cfg.broker.verification.skip_cert_common_name_check = !validateServerCert;
|
||||
mqtt_cfg.broker.verification.skip_cert_common_name_check = !mqtt_controll_config.validateServerCert;
|
||||
}
|
||||
|
||||
if (clientCert.length() && clientKey.length()) {
|
||||
if (clientCert.length() && clientKey.length())
|
||||
{
|
||||
mqtt_cfg.credentials.authentication.certificate = clientCert.c_str();
|
||||
mqtt_cfg.credentials.authentication.certificate_len = clientCert.length() + 1;
|
||||
|
||||
@@ -329,77 +324,79 @@ int MQTT_Init() {
|
||||
mqtt_cfg.credentials.authentication.key_len = clientKey.length() + 1;
|
||||
}
|
||||
|
||||
if (user.length() && password.length()){
|
||||
mqtt_cfg.credentials.username = user.c_str();
|
||||
mqtt_cfg.credentials.authentication.password = password.c_str();
|
||||
if (mqtt_controll_config.user.length() && mqtt_controll_config.password.length())
|
||||
{
|
||||
mqtt_cfg.credentials.username = mqtt_controll_config.user.c_str();
|
||||
mqtt_cfg.credentials.authentication.password = mqtt_controll_config.password.c_str();
|
||||
}
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("MQTT Client Init");
|
||||
#endif
|
||||
|
||||
client = esp_mqtt_client_init(&mqtt_cfg);
|
||||
if (client)
|
||||
{
|
||||
esp_err_t ret = esp_mqtt_client_register_event(client, esp_mqtt_ID, mqtt_event_handler, client);
|
||||
esp_err_t ret = esp_mqtt_client_register_event(client, mqtt_controll_config.esp_mqtt_ID, mqtt_event_handler, client);
|
||||
if (ret != ESP_OK)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Could not register event (ret=" + std::to_string(ret) + ")!");
|
||||
mqtt_initialized = false;
|
||||
mqtt_controll_config.mqtt_initialized = false;
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("MQTT Client Start");
|
||||
#endif
|
||||
ret = esp_mqtt_client_start(client);
|
||||
if (ret != ESP_OK)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Client start failed (retval=" + std::to_string(ret) + ")!");
|
||||
mqtt_initialized = false;
|
||||
mqtt_controll_config.mqtt_initialized = false;
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Client started, waiting for established connection...");
|
||||
mqtt_initialized = true;
|
||||
mqtt_controll_config.mqtt_initialized = true;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "Init failed, no handle created!");
|
||||
mqtt_initialized = false;
|
||||
mqtt_controll_config.mqtt_initialized = false;
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void MQTTdestroy_client(bool _disable = false) {
|
||||
if (client) {
|
||||
if (mqtt_connected) {
|
||||
void MQTTdestroy_client(bool _disable = false)
|
||||
{
|
||||
if (client)
|
||||
{
|
||||
if (mqtt_controll_config.mqtt_connected)
|
||||
{
|
||||
MQTTdestroySubscribeFunction();
|
||||
esp_mqtt_client_disconnect(client);
|
||||
mqtt_connected = false;
|
||||
mqtt_controll_config.mqtt_connected = false;
|
||||
}
|
||||
esp_mqtt_client_stop(client);
|
||||
esp_mqtt_client_destroy(client);
|
||||
client = NULL;
|
||||
mqtt_initialized = false;
|
||||
mqtt_controll_config.mqtt_initialized = false;
|
||||
}
|
||||
|
||||
if (_disable) // Disable MQTT service, avoid restart with MQTTPublish
|
||||
mqtt_configOK = false;
|
||||
if (_disable)
|
||||
{
|
||||
// Disable MQTT service, avoid restart with MQTTPublish
|
||||
mqtt_controll_config.mqtt_configOK = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool getMQTTisEnabled() {
|
||||
return mqtt_enabled;
|
||||
bool getMQTTisEnabled()
|
||||
{
|
||||
return mqtt_controll_config.mqtt_enabled;
|
||||
}
|
||||
|
||||
bool getMQTTisConnected() {
|
||||
return mqtt_connected;
|
||||
bool getMQTTisConnected()
|
||||
{
|
||||
return mqtt_controll_config.mqtt_connected;
|
||||
}
|
||||
|
||||
bool mqtt_handler_flow_start(std::string _topic, char* _data, int _data_len)
|
||||
bool mqtt_handler_flow_start(std::string _topic, char *_data, int _data_len)
|
||||
{
|
||||
ESP_LOGD(TAG, "Handler called: topic %s, data %.*s", _topic.c_str(), _data_len, _data);
|
||||
|
||||
@@ -407,47 +404,60 @@ bool mqtt_handler_flow_start(std::string _topic, char* _data, int _data_len)
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
bool mqtt_handler_set_prevalue(std::string _topic, char* _data, int _data_len)
|
||||
bool mqtt_handler_set_prevalue(std::string _topic, char *_data, int _data_len)
|
||||
{
|
||||
//ESP_LOGD(TAG, "Handler called: topic %s, data %.*s", _topic.c_str(), _data_len, _data);
|
||||
//example: {"numbersname": "main", "value": 12345.1234567}
|
||||
// ESP_LOGD(TAG, "Handler called: topic %s, data %.*s", _topic.c_str(), _data_len, _data);
|
||||
// example: {"numbersname": "main", "value": 12345.1234567}
|
||||
|
||||
if (_data_len > 0) { // Check if data length > 0
|
||||
if (_data_len > 0)
|
||||
{
|
||||
// Check if data length > 0
|
||||
cJSON *jsonData = cJSON_Parse(_data);
|
||||
cJSON *numbersname = cJSON_GetObjectItemCaseSensitive(jsonData, "numbersname");
|
||||
cJSON *value = cJSON_GetObjectItemCaseSensitive(jsonData, "value");
|
||||
|
||||
if (cJSON_IsString(numbersname) && (numbersname->valuestring != NULL)) { // Check if numbersname is valid
|
||||
if (cJSON_IsNumber(value)) { // Check if value is a number
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "handler_set_prevalue called: numbersname: " + std::string(numbersname->valuestring) +
|
||||
", value: " + std::to_string(value->valuedouble));
|
||||
if (flowctrl.UpdatePrevalue(std::to_string(value->valuedouble), std::string(numbersname->valuestring), true)) {
|
||||
if (cJSON_IsString(numbersname) && (numbersname->valuestring != NULL))
|
||||
{
|
||||
// Check if numbersname is valid
|
||||
if (cJSON_IsNumber(value))
|
||||
{
|
||||
// Check if value is a number
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "handler_set_prevalue called: numbersname: " + std::string(numbersname->valuestring) + ", value: " + std::to_string(value->valuedouble));
|
||||
if (flowctrl.UpdatePrevalue(std::to_string(value->valuedouble), std::string(numbersname->valuestring), true))
|
||||
{
|
||||
cJSON_Delete(jsonData);
|
||||
return ESP_OK;
|
||||
}
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "handler_set_prevalue: value not a valid number (\"value\": 12345.12345)");
|
||||
}
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "handler_set_prevalue: numbersname not a valid string (\"numbersname\": \"main\")");
|
||||
}
|
||||
cJSON_Delete(jsonData);
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "handler_set_prevalue: handler called, but no data received");
|
||||
}
|
||||
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
void MQTTconnected(){
|
||||
if (mqtt_connected) {
|
||||
void MQTTconnected()
|
||||
{
|
||||
if (mqtt_controll_config.mqtt_connected)
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Connected to broker");
|
||||
|
||||
if (connectFunktionMap != NULL) {
|
||||
for(std::map<std::string, std::function<void()>>::iterator it = connectFunktionMap->begin(); it != connectFunktionMap->end(); ++it) {
|
||||
if (connectFunktionMap != NULL)
|
||||
{
|
||||
for (std::map<std::string, std::function<void()>>::iterator it = connectFunktionMap->begin(); it != connectFunktionMap->end(); ++it)
|
||||
{
|
||||
it->second();
|
||||
ESP_LOGD(TAG, "call connect function %s", it->first.c_str());
|
||||
}
|
||||
@@ -456,58 +466,71 @@ void MQTTconnected(){
|
||||
// Subcribe to topics
|
||||
// Note: Further subsriptions are handled in GPIO class
|
||||
//*****************************************
|
||||
std::function<bool(std::string topic, char* data, int data_len)> subHandler1 = mqtt_handler_flow_start;
|
||||
MQTTregisterSubscribeFunction(maintopic + "/ctrl/flow_start", subHandler1); // subcribe to maintopic/ctrl/flow_start
|
||||
std::function<bool(std::string topic, char *data, int data_len)> subHandler1 = mqtt_handler_flow_start;
|
||||
MQTTregisterSubscribeFunction(mqtt_controll_config.maintopic + "/ctrl/flow_start", subHandler1); // subcribe to maintopic/ctrl/flow_start
|
||||
|
||||
std::function<bool(std::string topic, char* data, int data_len)> subHandler2 = mqtt_handler_set_prevalue;
|
||||
MQTTregisterSubscribeFunction(maintopic + "/ctrl/set_prevalue", subHandler2); // subcribe to maintopic/ctrl/set_prevalue
|
||||
std::function<bool(std::string topic, char *data, int data_len)> subHandler2 = mqtt_handler_set_prevalue;
|
||||
MQTTregisterSubscribeFunction(mqtt_controll_config.maintopic + "/ctrl/set_prevalue", subHandler2); // subcribe to maintopic/ctrl/set_prevalue
|
||||
|
||||
if (subscribeFunktionMap != NULL) {
|
||||
for(std::map<std::string, std::function<bool(std::string, char*, int)>>::iterator it = subscribeFunktionMap->begin(); it != subscribeFunktionMap->end(); ++it) {
|
||||
if (subscribeFunktionMap != NULL)
|
||||
{
|
||||
for (std::map<std::string, std::function<bool(std::string, char *, int)>>::iterator it = subscribeFunktionMap->begin(); it != subscribeFunktionMap->end(); ++it)
|
||||
{
|
||||
int msg_id = esp_mqtt_client_subscribe(client, it->first.c_str(), 0);
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "topic " + it->first + " subscribe successful");
|
||||
}
|
||||
}
|
||||
|
||||
/* Send Static Topics and Homeassistant Discovery */
|
||||
if (callbackOnConnected) { // Call onConnected callback routine --> mqtt_server
|
||||
callbackOnConnected(maintopic, SetRetainFlag);
|
||||
if (callbackOnConnected)
|
||||
{
|
||||
// Call onConnected callback routine --> mqtt_server
|
||||
callbackOnConnected(mqtt_controll_config.maintopic, mqtt_controll_config.retainFlag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MQTTregisterConnectFunction(std::string name, std::function<void()> func){
|
||||
void MQTTregisterConnectFunction(std::string name, std::function<void()> func)
|
||||
{
|
||||
ESP_LOGD(TAG, "MQTTregisteronnectFunction %s\r\n", name.c_str());
|
||||
if (connectFunktionMap == NULL) {
|
||||
if (connectFunktionMap == NULL)
|
||||
{
|
||||
connectFunktionMap = new std::map<std::string, std::function<void()>>();
|
||||
}
|
||||
|
||||
if ((*connectFunktionMap)[name] != NULL) {
|
||||
if ((*connectFunktionMap)[name] != NULL)
|
||||
{
|
||||
ESP_LOGW(TAG, "connect function %s already registred", name.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
(*connectFunktionMap)[name] = func;
|
||||
|
||||
if (mqtt_connected) {
|
||||
if (mqtt_controll_config.mqtt_connected)
|
||||
{
|
||||
func();
|
||||
}
|
||||
}
|
||||
|
||||
void MQTTunregisterConnectFunction(std::string name){
|
||||
void MQTTunregisterConnectFunction(std::string name)
|
||||
{
|
||||
ESP_LOGD(TAG, "unregisterConnnectFunction %s\r\n", name.c_str());
|
||||
if ((connectFunktionMap != NULL) && (connectFunktionMap->find(name) != connectFunktionMap->end())) {
|
||||
if ((connectFunktionMap != NULL) && (connectFunktionMap->find(name) != connectFunktionMap->end()))
|
||||
{
|
||||
connectFunktionMap->erase(name);
|
||||
}
|
||||
}
|
||||
|
||||
void MQTTregisterSubscribeFunction(std::string topic, std::function<bool(std::string, char*, int)> func){
|
||||
void MQTTregisterSubscribeFunction(std::string topic, std::function<bool(std::string, char *, int)> func)
|
||||
{
|
||||
ESP_LOGD(TAG, "registerSubscribeFunction %s", topic.c_str());
|
||||
if (subscribeFunktionMap == NULL) {
|
||||
subscribeFunktionMap = new std::map<std::string, std::function<bool(std::string, char*, int)>>();
|
||||
if (subscribeFunktionMap == NULL)
|
||||
{
|
||||
subscribeFunktionMap = new std::map<std::string, std::function<bool(std::string, char *, int)>>();
|
||||
}
|
||||
|
||||
if ((*subscribeFunktionMap)[topic] != NULL) {
|
||||
if ((*subscribeFunktionMap)[topic] != NULL)
|
||||
{
|
||||
ESP_LOGW(TAG, "topic %s already registered for subscription", topic.c_str());
|
||||
return;
|
||||
}
|
||||
@@ -515,10 +538,14 @@ void MQTTregisterSubscribeFunction(std::string topic, std::function<bool(std::st
|
||||
(*subscribeFunktionMap)[topic] = func;
|
||||
}
|
||||
|
||||
void MQTTdestroySubscribeFunction(){
|
||||
if (subscribeFunktionMap != NULL) {
|
||||
if (mqtt_connected) {
|
||||
for(std::map<std::string, std::function<bool(std::string, char*, int)>>::iterator it = subscribeFunktionMap->begin(); it != subscribeFunktionMap->end(); ++it) {
|
||||
void MQTTdestroySubscribeFunction()
|
||||
{
|
||||
if (subscribeFunktionMap != NULL)
|
||||
{
|
||||
if (mqtt_controll_config.mqtt_connected)
|
||||
{
|
||||
for (std::map<std::string, std::function<bool(std::string, char *, int)>>::iterator it = subscribeFunktionMap->begin(); it != subscribeFunktionMap->end(); ++it)
|
||||
{
|
||||
int msg_id = esp_mqtt_client_unsubscribe(client, it->first.c_str());
|
||||
ESP_LOGD(TAG, "topic %s unsubscribe successful, msg_id=%d", it->first.c_str(), msg_id);
|
||||
}
|
||||
@@ -529,4 +556,3 @@ void MQTTdestroySubscribeFunction(){
|
||||
subscribeFunktionMap = NULL;
|
||||
}
|
||||
}
|
||||
#endif //ENABLE_MQTT
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#ifdef ENABLE_MQTT
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef INTERFACE_MQTT_H
|
||||
@@ -9,23 +7,19 @@
|
||||
#include <map>
|
||||
#include <functional>
|
||||
|
||||
bool MQTT_Configure(std::string _mqttURI, std::string _clientid, std::string _user, std::string _password,
|
||||
std::string _maintopic, std::string _domoticz_in_topic, std::string _lwt, std::string _lwt_connected, std::string _lwt_disconnected,
|
||||
std::string _cacertfilename, bool _validateServerCert, std::string _clientcertfilename, std::string _clientkeyfilename,
|
||||
int _keepalive, bool SetRetainFlag, void *callbackOnConnected);
|
||||
bool MQTT_Configure(void *callbackOnConnected);
|
||||
int MQTT_Init();
|
||||
void MQTTdestroy_client(bool _disable);
|
||||
|
||||
bool MQTTPublish(std::string _key, std::string _content, int qos, bool retained_flag = 1); // retained Flag as Standart
|
||||
bool MQTTPublish(std::string _key, std::string _content, int qos, bool retained_flag = 1); // retained Flag as Standart
|
||||
|
||||
bool getMQTTisEnabled();
|
||||
bool getMQTTisConnected();
|
||||
|
||||
void MQTTregisterConnectFunction(std::string name, std::function<void()> func);
|
||||
void MQTTunregisterConnectFunction(std::string name);
|
||||
void MQTTregisterSubscribeFunction(std::string topic, std::function<bool(std::string, char*, int)> func);
|
||||
void MQTTregisterSubscribeFunction(std::string topic, std::function<bool(std::string, char *, int)> func);
|
||||
void MQTTdestroySubscribeFunction();
|
||||
void MQTTconnected();
|
||||
|
||||
#endif //INTERFACE_MQTT_H
|
||||
#endif //#ENABLE_MQTT
|
||||
#endif // INTERFACE_MQTT_H
|
||||
|
||||
@@ -1,305 +0,0 @@
|
||||
/* This is a modification of https://github.com/espressif/esp-mqtt/blob/master/lib/mqtt_outbox.c
|
||||
* to use the PSRAM instead of the internal heap.
|
||||
*/
|
||||
#include "mqtt_outbox.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "sys/queue.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_heap_caps.h"
|
||||
|
||||
/* Enable this to use the PSRAM for MQTT Publishing.
|
||||
* This saves 10 kBytes of RAM, see https://github.com/jomjol/AI-on-the-edge-device/pull/2113
|
||||
* However we can run into PSRAM fragmentation issues, leading to insufficient large blocks to load the model.
|
||||
* See https://github.com/jomjol/AI-on-the-edge-device/issues/2200 */
|
||||
#define USE_PSRAM
|
||||
|
||||
#ifdef CONFIG_MQTT_CUSTOM_OUTBOX
|
||||
static const char *TAG = "outbox";
|
||||
|
||||
typedef struct outbox_item {
|
||||
char *buffer;
|
||||
int len;
|
||||
int msg_id;
|
||||
int msg_type;
|
||||
int msg_qos;
|
||||
outbox_tick_t tick;
|
||||
pending_state_t pending;
|
||||
STAILQ_ENTRY(outbox_item) next;
|
||||
} outbox_item_t;
|
||||
|
||||
STAILQ_HEAD(outbox_list_t, outbox_item);
|
||||
|
||||
|
||||
outbox_handle_t outbox_init(void)
|
||||
{
|
||||
#ifdef USE_PSRAM
|
||||
outbox_handle_t outbox = heap_caps_calloc(1, sizeof(struct outbox_list_t), MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM);
|
||||
#else
|
||||
outbox_handle_t outbox = calloc(1, sizeof(struct outbox_list_t));
|
||||
#endif
|
||||
//ESP_MEM_CHECK(TAG, outbox, return NULL);
|
||||
STAILQ_INIT(outbox);
|
||||
return outbox;
|
||||
}
|
||||
|
||||
outbox_item_handle_t outbox_enqueue(outbox_handle_t outbox, outbox_message_handle_t message, outbox_tick_t tick)
|
||||
{
|
||||
#ifdef USE_PSRAM
|
||||
outbox_item_handle_t item = heap_caps_calloc(1, sizeof(outbox_item_t), MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM);
|
||||
#else
|
||||
outbox_item_handle_t item = calloc(1, sizeof(outbox_item_t));
|
||||
#endif
|
||||
//ESP_MEM_CHECK(TAG, item, return NULL);
|
||||
item->msg_id = message->msg_id;
|
||||
item->msg_type = message->msg_type;
|
||||
item->msg_qos = message->msg_qos;
|
||||
item->tick = tick;
|
||||
item->len = message->len + message->remaining_len;
|
||||
item->pending = QUEUED;
|
||||
#ifdef USE_PSRAM
|
||||
item->buffer = heap_caps_malloc(message->len + message->remaining_len, MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM);
|
||||
#else
|
||||
item->buffer = malloc(message->len + message->remaining_len);
|
||||
#endif
|
||||
/*ESP_MEM_CHECK(TAG, item->buffer, {
|
||||
free(item);
|
||||
return NULL;
|
||||
});*/
|
||||
memcpy(item->buffer, message->data, message->len);
|
||||
if (message->remaining_data) {
|
||||
memcpy(item->buffer + message->len, message->remaining_data, message->remaining_len);
|
||||
}
|
||||
STAILQ_INSERT_TAIL(outbox, item, next);
|
||||
ESP_LOGD(TAG, "ENQUEUE msgid=%d, msg_type=%d, len=%d, size=%d", message->msg_id, message->msg_type, message->len + message->remaining_len, outbox_get_size(outbox));
|
||||
return item;
|
||||
}
|
||||
|
||||
outbox_item_handle_t outbox_get(outbox_handle_t outbox, int msg_id)
|
||||
{
|
||||
outbox_item_handle_t item;
|
||||
STAILQ_FOREACH(item, outbox, next) {
|
||||
if (item->msg_id == msg_id) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
outbox_item_handle_t outbox_dequeue(outbox_handle_t outbox, pending_state_t pending, outbox_tick_t *tick)
|
||||
{
|
||||
outbox_item_handle_t item;
|
||||
STAILQ_FOREACH(item, outbox, next) {
|
||||
if (item->pending == pending) {
|
||||
if (tick) {
|
||||
*tick = item->tick;
|
||||
}
|
||||
return item;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
esp_err_t outbox_delete_item(outbox_handle_t outbox, outbox_item_handle_t item_to_delete)
|
||||
{
|
||||
outbox_item_handle_t item;
|
||||
STAILQ_FOREACH(item, outbox, next) {
|
||||
if (item == item_to_delete) {
|
||||
STAILQ_REMOVE(outbox, item, outbox_item, next);
|
||||
#ifdef USE_PSRAM
|
||||
heap_caps_free(item->buffer);
|
||||
heap_caps_free(item);
|
||||
#else
|
||||
free(item->buffer);
|
||||
free(item);
|
||||
#endif
|
||||
return ESP_OK;
|
||||
}
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
uint8_t *outbox_item_get_data(outbox_item_handle_t item, size_t *len, uint16_t *msg_id, int *msg_type, int *qos)
|
||||
{
|
||||
if (item) {
|
||||
*len = item->len;
|
||||
*msg_id = item->msg_id;
|
||||
*msg_type = item->msg_type;
|
||||
*qos = item->msg_qos;
|
||||
return (uint8_t *)item->buffer;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
esp_err_t outbox_delete(outbox_handle_t outbox, int msg_id, int msg_type)
|
||||
{
|
||||
outbox_item_handle_t item, tmp;
|
||||
STAILQ_FOREACH_SAFE(item, outbox, next, tmp) {
|
||||
if (item->msg_id == msg_id && (0xFF & (item->msg_type)) == msg_type) {
|
||||
STAILQ_REMOVE(outbox, item, outbox_item, next);
|
||||
#ifdef USE_PSRAM
|
||||
heap_caps_free(item->buffer);
|
||||
heap_caps_free(item);
|
||||
#else
|
||||
free(item->buffer);
|
||||
free(item);
|
||||
#endif
|
||||
ESP_LOGD(TAG, "DELETED msgid=%d, msg_type=%d, remain size=%d", msg_id, msg_type, outbox_get_size(outbox));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
esp_err_t outbox_delete_msgid(outbox_handle_t outbox, int msg_id)
|
||||
{
|
||||
outbox_item_handle_t item, tmp;
|
||||
STAILQ_FOREACH_SAFE(item, outbox, next, tmp) {
|
||||
if (item->msg_id == msg_id) {
|
||||
STAILQ_REMOVE(outbox, item, outbox_item, next);
|
||||
#ifdef USE_PSRAM
|
||||
heap_caps_free(item->buffer);
|
||||
heap_caps_free(item);
|
||||
#else
|
||||
free(item->buffer);
|
||||
free(item);
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
esp_err_t outbox_set_pending(outbox_handle_t outbox, int msg_id, pending_state_t pending)
|
||||
{
|
||||
outbox_item_handle_t item = outbox_get(outbox, msg_id);
|
||||
if (item) {
|
||||
item->pending = pending;
|
||||
return ESP_OK;
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
pending_state_t outbox_item_get_pending(outbox_item_handle_t item)
|
||||
{
|
||||
if (item) {
|
||||
return item->pending;
|
||||
}
|
||||
return QUEUED;
|
||||
}
|
||||
|
||||
esp_err_t outbox_set_tick(outbox_handle_t outbox, int msg_id, outbox_tick_t tick)
|
||||
{
|
||||
outbox_item_handle_t item = outbox_get(outbox, msg_id);
|
||||
if (item) {
|
||||
item->tick = tick;
|
||||
return ESP_OK;
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
esp_err_t outbox_delete_msgtype(outbox_handle_t outbox, int msg_type)
|
||||
{
|
||||
outbox_item_handle_t item, tmp;
|
||||
STAILQ_FOREACH_SAFE(item, outbox, next, tmp) {
|
||||
if (item->msg_type == msg_type) {
|
||||
STAILQ_REMOVE(outbox, item, outbox_item, next);
|
||||
#ifdef USE_PSRAM
|
||||
heap_caps_free(item->buffer);
|
||||
heap_caps_free(item);
|
||||
#else
|
||||
free(item->buffer);
|
||||
free(item);
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
int outbox_delete_single_expired(outbox_handle_t outbox, outbox_tick_t current_tick, outbox_tick_t timeout)
|
||||
{
|
||||
int msg_id = -1;
|
||||
outbox_item_handle_t item;
|
||||
STAILQ_FOREACH(item, outbox, next) {
|
||||
if (current_tick - item->tick > timeout) {
|
||||
STAILQ_REMOVE(outbox, item, outbox_item, next);
|
||||
|
||||
#ifdef USE_PSRAM
|
||||
heap_caps_free(item->buffer);
|
||||
#else
|
||||
free(item->buffer);
|
||||
#endif
|
||||
|
||||
msg_id = item->msg_id;
|
||||
|
||||
#ifdef USE_PSRAM
|
||||
heap_caps_free(item);
|
||||
#else
|
||||
free(item);
|
||||
#endif
|
||||
|
||||
return msg_id;
|
||||
}
|
||||
|
||||
}
|
||||
return msg_id;
|
||||
}
|
||||
|
||||
int outbox_delete_expired(outbox_handle_t outbox, outbox_tick_t current_tick, outbox_tick_t timeout)
|
||||
{
|
||||
int deleted_items = 0;
|
||||
outbox_item_handle_t item, tmp;
|
||||
STAILQ_FOREACH_SAFE(item, outbox, next, tmp) {
|
||||
if (current_tick - item->tick > timeout) {
|
||||
STAILQ_REMOVE(outbox, item, outbox_item, next);
|
||||
#ifdef USE_PSRAM
|
||||
heap_caps_free(item->buffer);
|
||||
heap_caps_free(item);
|
||||
#else
|
||||
free(item->buffer);
|
||||
free(item);
|
||||
#endif
|
||||
deleted_items ++;
|
||||
}
|
||||
|
||||
}
|
||||
return deleted_items;
|
||||
}
|
||||
|
||||
int outbox_get_size(outbox_handle_t outbox)
|
||||
{
|
||||
int siz = 0;
|
||||
outbox_item_handle_t item;
|
||||
STAILQ_FOREACH(item, outbox, next) {
|
||||
// Suppressing "use after free" warning as this could happen only if queue is in inconsistent state
|
||||
// which never happens if STAILQ interface used
|
||||
siz += item->len; // NOLINT(clang-analyzer-unix.Malloc)
|
||||
}
|
||||
return siz;
|
||||
}
|
||||
|
||||
void outbox_delete_all_items(outbox_handle_t outbox)
|
||||
{
|
||||
outbox_item_handle_t item, tmp;
|
||||
STAILQ_FOREACH_SAFE(item, outbox, next, tmp) {
|
||||
STAILQ_REMOVE(outbox, item, outbox_item, next);
|
||||
#ifdef USE_PSRAM
|
||||
heap_caps_free(item->buffer);
|
||||
heap_caps_free(item);
|
||||
#else
|
||||
free(item->buffer);
|
||||
free(item);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
void outbox_destroy(outbox_handle_t outbox)
|
||||
{
|
||||
outbox_delete_all_items(outbox);
|
||||
|
||||
#ifdef USE_PSRAM
|
||||
heap_caps_free(outbox);
|
||||
#else
|
||||
free(outbox);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* CONFIG_MQTT_CUSTOM_OUTBOX */
|
||||
@@ -1,66 +0,0 @@
|
||||
/* This is an adaption of https://github.com/espressif/esp-mqtt/blob/master/lib/include/mqtt_outbox.h
|
||||
* This file is subject to the terms and conditions defined in
|
||||
* file 'LICENSE', which is part of this source code package.
|
||||
* Tuan PM <tuanpm at live dot com>
|
||||
*/
|
||||
#ifndef _MQTT_OUTOBX_H_
|
||||
#define _MQTT_OUTOBX_H_
|
||||
//#include "platform.h"
|
||||
#include "esp_err.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct outbox_item;
|
||||
|
||||
typedef struct outbox_list_t *outbox_handle_t;
|
||||
typedef struct outbox_item *outbox_item_handle_t;
|
||||
typedef struct outbox_message *outbox_message_handle_t;
|
||||
typedef long long outbox_tick_t;
|
||||
|
||||
typedef struct outbox_message {
|
||||
uint8_t *data;
|
||||
int len;
|
||||
int msg_id;
|
||||
int msg_qos;
|
||||
int msg_type;
|
||||
uint8_t *remaining_data;
|
||||
int remaining_len;
|
||||
} outbox_message_t;
|
||||
|
||||
typedef enum pending_state {
|
||||
QUEUED,
|
||||
TRANSMITTED,
|
||||
ACKNOWLEDGED,
|
||||
CONFIRMED
|
||||
} pending_state_t;
|
||||
|
||||
outbox_handle_t outbox_init(void);
|
||||
outbox_item_handle_t outbox_enqueue(outbox_handle_t outbox, outbox_message_handle_t message, outbox_tick_t tick);
|
||||
outbox_item_handle_t outbox_dequeue(outbox_handle_t outbox, pending_state_t pending, outbox_tick_t *tick);
|
||||
outbox_item_handle_t outbox_get(outbox_handle_t outbox, int msg_id);
|
||||
uint8_t *outbox_item_get_data(outbox_item_handle_t item, size_t *len, uint16_t *msg_id, int *msg_type, int *qos);
|
||||
esp_err_t outbox_delete(outbox_handle_t outbox, int msg_id, int msg_type);
|
||||
esp_err_t outbox_delete_msgid(outbox_handle_t outbox, int msg_id);
|
||||
esp_err_t outbox_delete_msgtype(outbox_handle_t outbox, int msg_type);
|
||||
esp_err_t outbox_delete_item(outbox_handle_t outbox, outbox_item_handle_t item);
|
||||
int outbox_delete_expired(outbox_handle_t outbox, outbox_tick_t current_tick, outbox_tick_t timeout);
|
||||
/**
|
||||
* @brief Deletes single expired message returning it's message id
|
||||
*
|
||||
* @return msg id of the deleted message, -1 if no expired message in the outbox
|
||||
*/
|
||||
int outbox_delete_single_expired(outbox_handle_t outbox, outbox_tick_t current_tick, outbox_tick_t timeout);
|
||||
|
||||
esp_err_t outbox_set_pending(outbox_handle_t outbox, int msg_id, pending_state_t pending);
|
||||
pending_state_t outbox_item_get_pending(outbox_item_handle_t item);
|
||||
esp_err_t outbox_set_tick(outbox_handle_t outbox, int msg_id, outbox_tick_t tick);
|
||||
int outbox_get_size(outbox_handle_t outbox);
|
||||
void outbox_destroy(outbox_handle_t outbox);
|
||||
void outbox_delete_all_items(outbox_handle_t outbox);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@@ -1,93 +1,83 @@
|
||||
#ifdef ENABLE_MQTT
|
||||
#include "defines.h"
|
||||
|
||||
#include "server_mqtt.h"
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <vector>
|
||||
#include <esp_log.h>
|
||||
|
||||
#include "defines.h"
|
||||
#include "Helper.h"
|
||||
|
||||
#include "connect_wifi_sta.h"
|
||||
#include "read_network_config.h"
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "ClassLogFile.h"
|
||||
#include "connect_wlan.h"
|
||||
#include "read_wlanini.h"
|
||||
#include "server_mqtt.h"
|
||||
#include "ClassFlowMQTT.h"
|
||||
#include "interface_mqtt.h"
|
||||
#include "time_sntp.h"
|
||||
#include "../../include/defines.h"
|
||||
#include "basic_auth.h"
|
||||
|
||||
|
||||
|
||||
static const char *TAG = "MQTT SERVER";
|
||||
|
||||
|
||||
extern const char* libfive_git_version(void);
|
||||
extern const char* libfive_git_revision(void);
|
||||
extern const char* libfive_git_branch(void);
|
||||
extern const char *libfive_git_version(void);
|
||||
extern const char *libfive_git_revision(void);
|
||||
extern const char *libfive_git_branch(void);
|
||||
extern std::string getFwVersion(void);
|
||||
|
||||
std::vector<NumberPost*>* NUMBERS;
|
||||
bool HomeassistantDiscovery = false;
|
||||
std::string meterType = "";
|
||||
std::string valueUnit = "";
|
||||
std::string timeUnit = "";
|
||||
std::string rateUnit = "Unit/Minute";
|
||||
float roundInterval; // Minutes
|
||||
int keepAlive = 0; // Seconds
|
||||
bool retainFlag;
|
||||
static std::string maintopic, domoticzintopic;
|
||||
std::vector<NumberPost *> *NUMBERS;
|
||||
|
||||
bool sendingOf_DiscoveryAndStaticTopics_scheduled = true; // Set it to true to make sure it gets sent at least once after startup
|
||||
|
||||
|
||||
|
||||
void mqttServer_setParameter(std::vector<NumberPost*>* _NUMBERS, int _keepAlive, float _roundInterval) {
|
||||
void mqttServer_setParameter(std::vector<NumberPost *> *_NUMBERS)
|
||||
{
|
||||
NUMBERS = _NUMBERS;
|
||||
keepAlive = _keepAlive;
|
||||
roundInterval = _roundInterval;
|
||||
}
|
||||
|
||||
void mqttServer_setMeterType(std::string _meterType, std::string _valueUnit, std::string _timeUnit,std::string _rateUnit) {
|
||||
meterType = _meterType;
|
||||
valueUnit = _valueUnit;
|
||||
timeUnit = _timeUnit;
|
||||
rateUnit = _rateUnit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes any multi-level MQTT-topic and returns the last topic level as nodeId
|
||||
* see https://www.hivemq.com/blog/mqtt-essentials-part-5-mqtt-topics-best-practices/ for details about MQTT topics
|
||||
*/
|
||||
std::string createNodeId(std::string &topic) {
|
||||
*/
|
||||
std::string createNodeId(std::string &topic)
|
||||
{
|
||||
auto splitPos = topic.find_last_of('/');
|
||||
return (splitPos == std::string::npos) ? topic : topic.substr(splitPos + 1);
|
||||
}
|
||||
|
||||
bool sendHomeAssistantDiscoveryTopic(std::string group, std::string field,
|
||||
std::string name, std::string icon, std::string unit, std::string deviceClass, std::string stateClass, std::string entityCategory,
|
||||
int qos) {
|
||||
bool sendHomeAssistantDiscoveryTopic(std::string group, std::string field, std::string name, std::string icon, std::string unit, std::string deviceClass, std::string stateClass, std::string entityCategory, int qos)
|
||||
{
|
||||
std::string version = std::string(libfive_git_version());
|
||||
|
||||
if (version == "") {
|
||||
if (version == "")
|
||||
{
|
||||
version = std::string(libfive_git_branch()) + " (" + std::string(libfive_git_revision()) + ")";
|
||||
}
|
||||
|
||||
std::string topicFull;
|
||||
std::string configTopic;
|
||||
std::string payload;
|
||||
std::string component;
|
||||
std::string configTopic = field;
|
||||
|
||||
configTopic = field;
|
||||
|
||||
if (group != "" && (*NUMBERS).size() > 1) { // There is more than one meter, prepend the group so we can differentiate them
|
||||
if (group != "" && (*NUMBERS).size() > 1)
|
||||
{
|
||||
// There is more than one meter, prepend the group so we can differentiate them
|
||||
configTopic = group + "_" + field;
|
||||
name = group + " " + name;
|
||||
}
|
||||
|
||||
if (field == "problem") { // Special case: Binary sensor which is based on error topic
|
||||
std::string component;
|
||||
if (field == "problem")
|
||||
{
|
||||
// Special case: Binary sensor which is based on error topic
|
||||
component = "binary_sensor";
|
||||
}
|
||||
else if (field == "flowstart") { // Special case: Button
|
||||
else if (field == "flowstart")
|
||||
{
|
||||
// Special case: Button
|
||||
component = "button";
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
component = "sensor";
|
||||
}
|
||||
|
||||
@@ -96,296 +86,286 @@ bool sendHomeAssistantDiscoveryTopic(std::string group, std::string field,
|
||||
* <discovery_prefix>/<component>/[<node_id>/]<object_id>/config
|
||||
* if the main topic is embedded in a nested structure, we just use the last part as node_id
|
||||
* This means a maintopic "home/test/watermeter" is transformed to the discovery topic "homeassistant/sensor/watermeter/..."
|
||||
*/
|
||||
std::string node_id = createNodeId(maintopic);
|
||||
topicFull = "homeassistant/" + component + "/" + node_id + "/" + configTopic + "/config";
|
||||
*/
|
||||
std::string node_id = createNodeId(mqtt_controll_config.maintopic);
|
||||
topicFull = mqtt_controll_config.discoveryprefix + "/" + component + "/" + node_id + "/" + configTopic + "/config";
|
||||
|
||||
/* See https://www.home-assistant.io/docs/mqtt/discovery/ */
|
||||
payload = string("{") +
|
||||
"\"~\": \"" + maintopic + "\"," +
|
||||
"\"unique_id\": \"" + maintopic + "-" + configTopic + "\"," +
|
||||
"\"object_id\": \"" + maintopic + "_" + configTopic + "\"," + // Default entity ID; required for HA <= 2025.10
|
||||
"\"default_entity_id\": \"" + component + "." + maintopic + "_" + configTopic + "\"," + // Default entity ID; required in HA >=2026.4
|
||||
"\"name\": \"" + name + "\"," +
|
||||
"\"icon\": \"mdi:" + icon + "\",";
|
||||
std::string payload = string("{") + "\"~\": \"" + mqtt_controll_config.maintopic + "\"," +
|
||||
"\"unique_id\": \"" + mqtt_controll_config.maintopic + "-" + configTopic + "\"," +
|
||||
"\"object_id\": \"" + mqtt_controll_config.maintopic + "_" + configTopic + "\"," + // Default entity ID; required for HA <= 2025.10
|
||||
"\"default_entity_id\": \"" + component + "." + mqtt_controll_config.maintopic + "_" + configTopic + "\"," + // Default entity ID; required in HA >=2026.4
|
||||
"\"name\": \"" + name + "\"," + "\"icon\": \"mdi:" + icon + "\",";
|
||||
|
||||
if (group != "") {
|
||||
if (field == "problem") { // Special case: Binary sensor which is based on error topic
|
||||
if (group != "")
|
||||
{
|
||||
if (field == "problem")
|
||||
{
|
||||
// Special case: Binary sensor which is based on error topic
|
||||
payload += "\"state_topic\": \"~/" + group + "/error\",";
|
||||
payload += "\"value_template\": \"{{ 'OFF' if 'no error' in value else 'ON'}}\",";
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
payload += "\"state_topic\": \"~/" + group + "/" + field + "\",";
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (field == "problem") { // Special case: Binary sensor which is based on error topic
|
||||
else
|
||||
{
|
||||
if (field == "problem")
|
||||
{
|
||||
// Special case: Binary sensor which is based on error topic
|
||||
payload += "\"state_topic\": \"~/error\",";
|
||||
payload += "\"value_template\": \"{{ 'OFF' if 'no error' in value else 'ON'}}\",";
|
||||
}
|
||||
else if (field == "flowstart") { // Special case: Button
|
||||
else if (field == "flowstart")
|
||||
{
|
||||
// Special case: Button
|
||||
payload += "\"cmd_t\":\"~/ctrl/flow_start\","; // Add command topic
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
payload += "\"state_topic\": \"~/" + field + "\",";
|
||||
}
|
||||
}
|
||||
|
||||
if (unit != "") {
|
||||
if (unit != "")
|
||||
{
|
||||
payload += "\"unit_of_meas\": \"" + unit + "\",";
|
||||
}
|
||||
|
||||
if (deviceClass != "") {
|
||||
if (deviceClass != "")
|
||||
{
|
||||
payload += "\"device_class\": \"" + deviceClass + "\",";
|
||||
}
|
||||
|
||||
if (stateClass != "") {
|
||||
if (stateClass != "")
|
||||
{
|
||||
payload += "\"state_class\": \"" + stateClass + "\",";
|
||||
}
|
||||
|
||||
if (entityCategory != "") {
|
||||
if (entityCategory != "")
|
||||
{
|
||||
payload += "\"entity_category\": \"" + entityCategory + "\",";
|
||||
}
|
||||
|
||||
payload +=
|
||||
"\"availability_topic\": \"~/" + std::string(LWT_TOPIC) + "\"," +
|
||||
"\"payload_available\": \"" + LWT_CONNECTED + "\"," +
|
||||
"\"availability_topic\": \"~/" + std::string(LWT_TOPIC) + "\"," +
|
||||
"\"payload_available\": \"" + LWT_CONNECTED + "\"," +
|
||||
"\"payload_not_available\": \"" + LWT_DISCONNECTED + "\",";
|
||||
|
||||
payload += string("\"device\": {") +
|
||||
"\"identifiers\": [\"" + maintopic + "\"]," +
|
||||
"\"name\": \"" + maintopic + "\"," +
|
||||
"\"model\": \"Meter Digitizer\"," +
|
||||
"\"manufacturer\": \"AI on the Edge Device\"," +
|
||||
"\"sw_version\": \"" + version + "\"," +
|
||||
"\"configuration_url\": \"http://" + *getIPAddress() + "\"" +
|
||||
"}" +
|
||||
"}";
|
||||
payload += string("\"device\": {") + "\"identifiers\": [\"" + mqtt_controll_config.maintopic + "\"]," +
|
||||
"\"name\": \"" + mqtt_controll_config.maintopic + "\"," +
|
||||
"\"model\": \"Meter Digitizer\"," +
|
||||
"\"manufacturer\": \"AI on the Edge Device\"," +
|
||||
"\"sw_version\": \"" + version + "\"," +
|
||||
"\"configuration_url\": \"http://" + network_config.ipaddress + "\"" + "}" + "}";
|
||||
|
||||
return MQTTPublish(topicFull, payload, qos, true);
|
||||
}
|
||||
|
||||
bool MQTThomeassistantDiscovery(int qos) {
|
||||
bool allSendsSuccessed = false;
|
||||
|
||||
if (!getMQTTisConnected()) {
|
||||
bool MQTThomeassistantDiscovery(int qos)
|
||||
{
|
||||
if (!getMQTTisConnected())
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Unable to send Homeassistant Discovery Topics, we are not connected to the MQTT broker!");
|
||||
return false;
|
||||
}
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Publishing Homeassistant Discovery topics (Meter Type: '" + meterType + "', Value Unit: '" + valueUnit + "' , Rate Unit: '" + rateUnit + "') ...");
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Publishing Homeassistant Discovery topics (Meter Type: '" + mqtt_controll_config.meterType + "', Value Unit: '" + mqtt_controll_config.valueUnit + "' , Rate Unit: '" + mqtt_controll_config.rateUnit + "') ...");
|
||||
|
||||
int aFreeInternalHeapSizeBefore = heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
|
||||
bool allSendsSuccessed = false;
|
||||
int aFreeInternalHeapSizeBefore = heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
|
||||
|
||||
// Group | Field | User Friendly Name | Icon | Unit | Device Class | State Class | Entity Category
|
||||
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "uptime", "Uptime", "progress-clock", "s", "duration", "measurement", "diagnostic", qos);
|
||||
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "MAC", "MAC Address", "network-outline", "", "", "", "diagnostic", qos);
|
||||
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "fwVersion", "Firmware Version", "application-outline", "", "", "", "diagnostic", qos);
|
||||
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "hostname", "Hostname", "network-outline", "", "", "", "diagnostic", qos);
|
||||
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "freeMem", "Free Memory", "memory", "B", "", "measurement", "diagnostic", qos);
|
||||
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "wifiRSSI", "Wi-Fi RSSI", "wifi", "dBm", "signal_strength", "", "diagnostic", qos);
|
||||
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "CPUtemp", "CPU Temperature", "thermometer", "°C", "temperature", "measurement", "diagnostic", qos);
|
||||
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "interval", "Interval", "clock-time-eight-outline", "min", "", "measurement", "diagnostic", qos);
|
||||
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "IP", "IP", "network-outline", "", "", "", "diagnostic", qos);
|
||||
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "status", "Status", "list-status", "", "", "", "diagnostic", qos);
|
||||
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "flowstart", "Manual Flow Start", "timer-play-outline", "", "", "", "", qos);
|
||||
// Group | Field | User Friendly Name | Icon | Unit | Device Class | State Class | Entity Category
|
||||
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "uptime", "Uptime", "progress-clock", "s", "duration", "measurement", "diagnostic", qos);
|
||||
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "MAC", "MAC Address", "network-outline", "", "", "", "diagnostic", qos);
|
||||
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "fwVersion", "Firmware Version", "application-outline", "", "", "", "diagnostic", qos);
|
||||
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "hostname", "Hostname", "network-outline", "", "", "", "diagnostic", qos);
|
||||
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "freeMem", "Free Memory", "memory", "B", "", "measurement", "diagnostic", qos);
|
||||
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "wifiRSSI", "Wi-Fi RSSI", "wifi", "dBm", "signal_strength", "", "diagnostic", qos);
|
||||
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "CPUtemp", "CPU Temperature", "thermometer", "°C", "temperature", "measurement", "diagnostic", qos);
|
||||
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "interval", "Interval", "clock-time-eight-outline", "min", "", "measurement", "diagnostic", qos);
|
||||
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "IP", "IP", "network-outline", "", "", "", "diagnostic", qos);
|
||||
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "status", "Status", "list-status", "", "", "", "diagnostic", qos);
|
||||
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic("", "flowstart", "Manual Flow Start", "timer-play-outline", "", "", "", "", qos);
|
||||
|
||||
|
||||
for (int i = 0; i < (*NUMBERS).size(); ++i) {
|
||||
for (int i = 0; i < (*NUMBERS).size(); ++i)
|
||||
{
|
||||
std::string group = (*NUMBERS)[i]->name;
|
||||
if (group == "default") {
|
||||
if (group == "default")
|
||||
{
|
||||
group = "";
|
||||
}
|
||||
|
||||
/* If "Allow neg. rate" is true, use "measurement" instead of "total_increasing" for the State Class, see https://github.com/jomjol/AI-on-the-edge-device/issues/3331 */
|
||||
std::string value_state_class = "total_increasing";
|
||||
if (meterType == "temperature") {
|
||||
if (mqtt_controll_config.meterType == "temperature")
|
||||
{
|
||||
value_state_class = "measurement";
|
||||
}
|
||||
else if ((*NUMBERS)[i]->AllowNegativeRates) {
|
||||
else if ((*NUMBERS)[i]->AllowNegativeRates)
|
||||
{
|
||||
value_state_class = "total";
|
||||
}
|
||||
|
||||
/* Energy meters need a different Device Class, see https://github.com/jomjol/AI-on-the-edge-device/issues/3333 */
|
||||
std::string rate_device_class = "volume_flow_rate";
|
||||
if (meterType == "energy") {
|
||||
if (mqtt_controll_config.meterType == "energy")
|
||||
{
|
||||
rate_device_class = "power";
|
||||
}
|
||||
|
||||
// Group | Field | User Friendly Name | Icon | Unit | Device Class | State Class | Entity Category | QoS
|
||||
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "value", "Value", "gauge", valueUnit, meterType, value_state_class, "", qos); // State Class = "total_increasing" if <NUMBERS>.AllowNegativeRates = false, "measurement" in case of a thermometer, else use "total".
|
||||
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "raw", "Raw Value", "raw", valueUnit, meterType, value_state_class, "diagnostic", qos);
|
||||
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "error", "Error", "alert-circle-outline", "", "", "", "diagnostic", qos);
|
||||
// Group | Field | User Friendly Name | Icon | Unit | Device Class | State Class | Entity Category | QoS
|
||||
// State Class = "total_increasing" if <NUMBERS>.AllowNegativeRates = false, "measurement" in case of a thermometer, else use "total".
|
||||
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "value", "Value", "gauge", mqtt_controll_config.valueUnit, mqtt_controll_config.meterType, value_state_class, "", qos);
|
||||
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "raw", "Raw Value", "raw", mqtt_controll_config.valueUnit, mqtt_controll_config.meterType, value_state_class, "diagnostic", qos);
|
||||
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "error", "Error", "alert-circle-outline", "", "", "", "diagnostic", qos);
|
||||
/* Not announcing "rate" as it is better to use rate_per_time_unit resp. rate_per_digitization_round */
|
||||
// allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "rate", "Rate (Unit/Minute)", "swap-vertical", "", "", "", "", qos); // Legacy, always Unit per Minute
|
||||
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "rate_per_time_unit", "Rate (" + rateUnit + ")", "swap-vertical", rateUnit, rate_device_class, "measurement", "", qos);
|
||||
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "rate_per_digitization_round","Change since last Digitization round", "arrow-expand-vertical", valueUnit, "", "measurement", "", qos); // correctly the Unit is Unit/Interval!
|
||||
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "timestamp", "Timestamp", "clock-time-eight-outline", "", "timestamp", "", "diagnostic", qos);
|
||||
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "json", "JSON", "code-json", "", "", "", "diagnostic", qos);
|
||||
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "problem", "Problem", "alert-outline", "", "problem", "", "", qos); // Special binary sensor which is based on error topic
|
||||
// allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "rate", "Rate (Unit/Minute)", "swap-vertical", "", "", "", "", qos); // Legacy, always Unit per Minute
|
||||
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "rate_per_time_unit", "Rate (" + mqtt_controll_config.rateUnit + ")", "swap-vertical", mqtt_controll_config.rateUnit, rate_device_class, "measurement", "", qos);
|
||||
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "rate_per_digitization_round", "Change since last Digitization round", "arrow-expand-vertical", mqtt_controll_config.valueUnit, "", "measurement", "", qos); // correctly the Unit is Unit/Interval!
|
||||
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "timestamp", "Timestamp", "clock-time-eight-outline", "", "timestamp", "", "diagnostic", qos);
|
||||
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "json", "JSON", "code-json", "", "", "", "diagnostic", qos);
|
||||
allSendsSuccessed |= sendHomeAssistantDiscoveryTopic(group, "problem", "Problem", "alert-outline", "", "problem", "", "", qos); // Special binary sensor which is based on error topic
|
||||
}
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Successfully published all Homeassistant Discovery MQTT topics");
|
||||
|
||||
int aFreeInternalHeapSizeAfter = heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
|
||||
int aMinFreeInternalHeapSize = heap_caps_get_minimum_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
|
||||
int aMinFreeInternalHeapSize = heap_caps_get_minimum_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Int. Heap Usage before Publishing Homeassistand Discovery Topics: " +
|
||||
to_string(aFreeInternalHeapSizeBefore) + ", after: " + to_string(aFreeInternalHeapSizeAfter) + ", delta: " +
|
||||
to_string(aFreeInternalHeapSizeBefore - aFreeInternalHeapSizeAfter) + ", lowest free: " + to_string(aMinFreeInternalHeapSize));
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Int. Heap Usage before Publishing Homeassistand Discovery Topics: " + to_string(aFreeInternalHeapSizeBefore) + ", after: " + to_string(aFreeInternalHeapSizeAfter) + ", delta: " + to_string(aFreeInternalHeapSizeBefore - aFreeInternalHeapSizeAfter) + ", lowest free: " + to_string(aMinFreeInternalHeapSize));
|
||||
|
||||
return allSendsSuccessed;
|
||||
}
|
||||
|
||||
bool publishSystemData(int qos) {
|
||||
bool allSendsSuccessed = false;
|
||||
|
||||
if (!getMQTTisConnected()) {
|
||||
bool publishSystemData(int qos)
|
||||
{
|
||||
if (!getMQTTisConnected())
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Unable to send System Topics, we are not connected to the MQTT broker!");
|
||||
return false;
|
||||
}
|
||||
|
||||
char tmp_char[50];
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Publishing System MQTT topics...");
|
||||
|
||||
int aFreeInternalHeapSizeBefore = heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
|
||||
char tmp_char[50];
|
||||
bool allSendsSuccessed = false;
|
||||
int aFreeInternalHeapSizeBefore = heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
|
||||
|
||||
allSendsSuccessed |= MQTTPublish(maintopic + "/" + std::string(LWT_TOPIC), LWT_CONNECTED, qos, retainFlag); // Publish "connected" to maintopic/connection
|
||||
allSendsSuccessed |= MQTTPublish(mqtt_controll_config.maintopic + "/" + std::string(LWT_TOPIC), LWT_CONNECTED, qos, mqtt_controll_config.retainFlag); // Publish "connected" to maintopic/connection
|
||||
|
||||
sprintf(tmp_char, "%ld", (long)getUpTime());
|
||||
allSendsSuccessed |= MQTTPublish(maintopic + "/" + "uptime", std::string(tmp_char), qos, retainFlag);
|
||||
sprintf(tmp_char, "%ld", (long)get_uptime());
|
||||
allSendsSuccessed |= MQTTPublish(mqtt_controll_config.maintopic + "/" + "uptime", std::string(tmp_char), qos, mqtt_controll_config.retainFlag);
|
||||
|
||||
sprintf(tmp_char, "%lu", (long) getESPHeapSize());
|
||||
allSendsSuccessed |= MQTTPublish(maintopic + "/" + "freeMem", std::string(tmp_char), qos, retainFlag);
|
||||
sprintf(tmp_char, "%lu", (long)get_heapsize());
|
||||
allSendsSuccessed |= MQTTPublish(mqtt_controll_config.maintopic + "/" + "freeMem", std::string(tmp_char), qos, mqtt_controll_config.retainFlag);
|
||||
|
||||
sprintf(tmp_char, "%d", get_WIFI_RSSI());
|
||||
allSendsSuccessed |= MQTTPublish(maintopic + "/" + "wifiRSSI", std::string(tmp_char), qos, retainFlag);
|
||||
sprintf(tmp_char, "%d", get_wifi_rssi());
|
||||
allSendsSuccessed |= MQTTPublish(mqtt_controll_config.maintopic + "/" + "wifiRSSI", std::string(tmp_char), qos, mqtt_controll_config.retainFlag);
|
||||
|
||||
sprintf(tmp_char, "%d", (int)temperatureRead());
|
||||
allSendsSuccessed |= MQTTPublish(maintopic + "/" + "CPUtemp", std::string(tmp_char), qos, retainFlag);
|
||||
sprintf(tmp_char, "%d", (int)read_tempsensor());
|
||||
allSendsSuccessed |= MQTTPublish(mqtt_controll_config.maintopic + "/" + "CPUtemp", std::string(tmp_char), qos, mqtt_controll_config.retainFlag);
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Successfully published all System MQTT topics");
|
||||
|
||||
int aFreeInternalHeapSizeAfter = heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
|
||||
int aMinFreeInternalHeapSize = heap_caps_get_minimum_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
|
||||
int aFreeInternalHeapSizeAfter = heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
|
||||
int aMinFreeInternalHeapSize = heap_caps_get_minimum_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Int. Heap Usage before publishing System Topics: " +
|
||||
to_string(aFreeInternalHeapSizeBefore) + ", after: " + to_string(aFreeInternalHeapSizeAfter) + ", delta: " +
|
||||
to_string(aFreeInternalHeapSizeBefore - aFreeInternalHeapSizeAfter) + ", lowest free: " + to_string(aMinFreeInternalHeapSize));
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Int. Heap Usage before publishing System Topics: " + to_string(aFreeInternalHeapSizeBefore) + ", after: " + to_string(aFreeInternalHeapSizeAfter) + ", delta: " + to_string(aFreeInternalHeapSizeBefore - aFreeInternalHeapSizeAfter) + ", lowest free: " + to_string(aMinFreeInternalHeapSize));
|
||||
|
||||
return allSendsSuccessed;
|
||||
}
|
||||
|
||||
|
||||
bool publishStaticData(int qos) {
|
||||
bool allSendsSuccessed = false;
|
||||
|
||||
if (!getMQTTisConnected()) {
|
||||
bool publishStaticData(int qos)
|
||||
{
|
||||
if (!getMQTTisConnected())
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Unable to send Static Topics, we are not connected to the MQTT broker!");
|
||||
return false;
|
||||
}
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Publishing static MQTT topics...");
|
||||
|
||||
int aFreeInternalHeapSizeBefore = heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
|
||||
bool allSendsSuccessed = false;
|
||||
int aFreeInternalHeapSizeBefore = heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
|
||||
|
||||
allSendsSuccessed |= MQTTPublish(maintopic + "/" + "fwVersion", getFwVersion().c_str(), qos, retainFlag);
|
||||
allSendsSuccessed |= MQTTPublish(maintopic + "/" + "MAC", getMac(), qos, retainFlag);
|
||||
allSendsSuccessed |= MQTTPublish(maintopic + "/" + "IP", *getIPAddress(), qos, retainFlag);
|
||||
allSendsSuccessed |= MQTTPublish(maintopic + "/" + "hostname", wlan_config.hostname, qos, retainFlag);
|
||||
allSendsSuccessed |= MQTTPublish(mqtt_controll_config.maintopic + "/" + "fwVersion", getFwVersion().c_str(), qos, mqtt_controll_config.retainFlag);
|
||||
allSendsSuccessed |= MQTTPublish(mqtt_controll_config.maintopic + "/" + "MAC", get_mac(), qos, mqtt_controll_config.retainFlag);
|
||||
allSendsSuccessed |= MQTTPublish(mqtt_controll_config.maintopic + "/" + "IP", network_config.ipaddress, qos, mqtt_controll_config.retainFlag);
|
||||
allSendsSuccessed |= MQTTPublish(mqtt_controll_config.maintopic + "/" + "hostname", network_config.hostname, qos, mqtt_controll_config.retainFlag);
|
||||
|
||||
std::stringstream stream;
|
||||
stream << std::fixed << std::setprecision(1) << roundInterval; // minutes
|
||||
allSendsSuccessed |= MQTTPublish(maintopic + "/" + "interval", stream.str(), qos, retainFlag);
|
||||
stream << std::fixed << std::setprecision(1) << mqtt_controll_config.roundInterval; // minutes
|
||||
allSendsSuccessed |= MQTTPublish(mqtt_controll_config.maintopic + "/" + "interval", stream.str(), qos, mqtt_controll_config.retainFlag);
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Successfully published all Static MQTT topics");
|
||||
|
||||
int aFreeInternalHeapSizeAfter = heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
|
||||
int aMinFreeInternalHeapSize = heap_caps_get_minimum_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
|
||||
int aFreeInternalHeapSizeAfter = heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
|
||||
int aMinFreeInternalHeapSize = heap_caps_get_minimum_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
|
||||
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Int. Heap Usage before Publishing Static Topics: " +
|
||||
to_string(aFreeInternalHeapSizeBefore) + ", after: " + to_string(aFreeInternalHeapSizeAfter) + ", delta: " +
|
||||
to_string(aFreeInternalHeapSizeBefore - aFreeInternalHeapSizeAfter) + ", lowest free: " + to_string(aMinFreeInternalHeapSize));
|
||||
LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "Int. Heap Usage before Publishing Static Topics: " + to_string(aFreeInternalHeapSizeBefore) + ", after: " + to_string(aFreeInternalHeapSizeAfter) + ", delta: " + to_string(aFreeInternalHeapSizeBefore - aFreeInternalHeapSizeAfter) + ", lowest free: " + to_string(aMinFreeInternalHeapSize));
|
||||
|
||||
return allSendsSuccessed;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t scheduleSendingDiscovery_and_static_Topics(httpd_req_t *req) {
|
||||
esp_err_t scheduleSendingDiscovery_and_static_Topics(httpd_req_t *req)
|
||||
{
|
||||
sendingOf_DiscoveryAndStaticTopics_scheduled = true;
|
||||
char msg[] = "MQTT Homeassistant Discovery and Static Topics scheduled";
|
||||
httpd_resp_send(req, msg, strlen(msg));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t sendDiscovery_and_static_Topics(void) {
|
||||
bool success = false;
|
||||
|
||||
if (!sendingOf_DiscoveryAndStaticTopics_scheduled) {
|
||||
esp_err_t sendDiscovery_and_static_Topics(void)
|
||||
{
|
||||
if (!sendingOf_DiscoveryAndStaticTopics_scheduled)
|
||||
{
|
||||
// Flag not set, nothing to do
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
if (HomeassistantDiscovery) {
|
||||
bool success = false;
|
||||
|
||||
if (mqtt_controll_config.HomeAssistantDiscovery)
|
||||
{
|
||||
success = MQTThomeassistantDiscovery(1);
|
||||
}
|
||||
|
||||
success |= publishStaticData(1);
|
||||
|
||||
if (success) { // Success, clear the flag
|
||||
if (success)
|
||||
{
|
||||
// Success, clear the flag
|
||||
sendingOf_DiscoveryAndStaticTopics_scheduled = false;
|
||||
return ESP_OK;
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "One or more MQTT topics failed to be published, will try sending them in the next round!");
|
||||
/* Keep sendingOf_DiscoveryAndStaticTopics_scheduled set so we can retry after the next round */
|
||||
return ESP_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GotConnected(std::string maintopic, bool retainFlag) {
|
||||
void GotConnected(std::string maintopic, bool retainFlag)
|
||||
{
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
void register_server_mqtt_uri(httpd_handle_t server) {
|
||||
httpd_uri_t uri = { };
|
||||
uri.method = HTTP_GET;
|
||||
std::string mqttServer_getMainTopic()
|
||||
{
|
||||
return mqtt_controll_config.maintopic;
|
||||
}
|
||||
|
||||
uri.uri = "/mqtt_publish_discovery";
|
||||
void mqtt_register_uri(httpd_handle_t server)
|
||||
{
|
||||
httpd_uri_t uri = {};
|
||||
uri.method = HTTP_GET;
|
||||
|
||||
uri.uri = "/mqtt_publish_discovery";
|
||||
uri.handler = APPLY_BASIC_AUTH_FILTER(scheduleSendingDiscovery_and_static_Topics);
|
||||
uri.user_ctx = (void*) "";
|
||||
uri.user_ctx = (void *)"";
|
||||
httpd_register_uri_handler(server, &uri);
|
||||
}
|
||||
|
||||
|
||||
std::string getTimeUnit(void) {
|
||||
return timeUnit;
|
||||
}
|
||||
|
||||
|
||||
void SetHomeassistantDiscoveryEnabled(bool enabled) {
|
||||
HomeassistantDiscovery = enabled;
|
||||
}
|
||||
|
||||
|
||||
void setMqtt_Server_Retain(bool _retainFlag) {
|
||||
retainFlag = _retainFlag;
|
||||
}
|
||||
|
||||
void mqttServer_setMainTopic( std::string _maintopic) {
|
||||
maintopic = _maintopic;
|
||||
}
|
||||
|
||||
std::string mqttServer_getMainTopic() {
|
||||
return maintopic;
|
||||
}
|
||||
|
||||
void mqttServer_setDmoticzInTopic( std::string _domoticzintopic) {
|
||||
domoticzintopic = _domoticzintopic;
|
||||
}
|
||||
|
||||
|
||||
#endif //ENABLE_MQTT
|
||||
|
||||
@@ -1,31 +1,19 @@
|
||||
#ifdef ENABLE_MQTT
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef SERVERMQTT_H
|
||||
#define SERVERMQTT_H
|
||||
#ifndef SERVER_MQTT_H
|
||||
#define SERVER_MQTT_H
|
||||
|
||||
#include "ClassFlowDefineTypes.h"
|
||||
|
||||
void SetHomeassistantDiscoveryEnabled(bool enabled);
|
||||
void mqttServer_setParameter(std::vector<NumberPost*>* _NUMBERS, int interval, float roundInterval);
|
||||
void mqttServer_setMeterType(std::string meterType, std::string valueUnit, std::string timeUnit,std::string rateUnit);
|
||||
void setMqtt_Server_Retain(bool SetRetainFlag);
|
||||
void mqttServer_setMainTopic( std::string maintopic);
|
||||
void mqttServer_setDmoticzInTopic( std::string domoticzintopic);
|
||||
|
||||
|
||||
void mqttServer_setParameter(std::vector<NumberPost *> *_NUMBERS);
|
||||
std::string mqttServer_getMainTopic();
|
||||
|
||||
void register_server_mqtt_uri(httpd_handle_t server);
|
||||
|
||||
bool publishSystemData(int qos);
|
||||
|
||||
std::string getTimeUnit(void);
|
||||
void GotConnected(std::string maintopic, bool SetRetainFlag);
|
||||
esp_err_t sendDiscovery_and_static_Topics(void);
|
||||
|
||||
std::string createNodeId(std::string &topic);
|
||||
|
||||
#endif //SERVERMQTT_H
|
||||
#endif //ENABLE_MQTT
|
||||
void mqtt_register_uri(httpd_handle_t server);
|
||||
|
||||
#endif // SERVER_MQTT_H
|
||||
|
||||
7
code/components/jomjol_network/CMakeLists.txt
Normal file
7
code/components/jomjol_network/CMakeLists.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
|
||||
|
||||
idf_component_register(SRCS ${app_sources}
|
||||
INCLUDE_DIRS "." "../../include"
|
||||
REQUIRES driver esp_eth esp_netif esp_wifi mdns nvs_flash jomjol_helper jomjol_mqtt wpa_supplicant)
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user