mirror of
https://github.com/EyeTrackVR/OpenIris.git
synced 2025-11-04 15:39:42 +08:00
Merge branch 'feature/mdns-autodiscovery' of https://github.com/lorow/OpenIris
This commit is contained in:
commit
982a067c8c
84
.github/workflows/build_release_bins.yml
vendored
Normal file
84
.github/workflows/build_release_bins.yml
vendored
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
name: Build and Release the OpenIris bin files
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- "mdns-autodiscovery"
|
||||||
|
- "master"
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- "master"
|
||||||
|
workflow_dispatch:
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
target_name: [
|
||||||
|
esp32AITinker,
|
||||||
|
esp32M5Stack,
|
||||||
|
esp32CAM,
|
||||||
|
esp_eye,
|
||||||
|
wrover,
|
||||||
|
]
|
||||||
|
target_build_type: [
|
||||||
|
"",
|
||||||
|
_release,
|
||||||
|
_OTA,
|
||||||
|
]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Cache pip
|
||||||
|
uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: ~/.cache/pip
|
||||||
|
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
|
||||||
|
restore-keys: ${{ runner.os }}-pip-
|
||||||
|
- name: Cache PlatformIO
|
||||||
|
uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: ~/.platformio
|
||||||
|
key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }}
|
||||||
|
- name: Setup Python install
|
||||||
|
uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: '3.x'
|
||||||
|
- name: Install PlatformIO with Pip
|
||||||
|
run: |
|
||||||
|
python -m pip install --upgrade pip
|
||||||
|
pip install --upgrade platformio
|
||||||
|
- name: Build OpenIris Firmware
|
||||||
|
working-directory: ./ESP
|
||||||
|
run: |
|
||||||
|
pio run --environment ${{ matrix.target_name }}${{ matrix.target_build_type }}
|
||||||
|
- name: Archive Version File
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: version
|
||||||
|
path: ./ESP/tools/version.txt
|
||||||
|
- name: Archive Firmware name File
|
||||||
|
# create an environment variable with the name of the firmware file by catting the firmware_name.txt file
|
||||||
|
run: |
|
||||||
|
echo "FIRMWARE_NAME=$(cat ./ESP/tools/firmware_name.txt)" >> $GITHUB_ENV
|
||||||
|
- name: Archive Firmware Files
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: ${{ matrix.target_name }}${{ matrix.target_build_type }}-firmware
|
||||||
|
path: "./ESP/.pio/build/${{ matrix.target_name }}${{ matrix.target_build_type }}/${{ env.FIRMWARE_NAME }}.bin"
|
||||||
|
if-no-files-found: error
|
||||||
|
release-openiris:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [build]
|
||||||
|
steps:
|
||||||
|
- name: Download Firmware Files
|
||||||
|
uses: actions/download-artifact@v2
|
||||||
|
with:
|
||||||
|
path: release
|
||||||
|
- name: Release Firmware
|
||||||
|
uses: ncipollo/release-action@v1
|
||||||
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
|
with:
|
||||||
|
artifacts: release/*/*.bin
|
||||||
|
generateReleaseNotes: true
|
||||||
|
allowUpdates: true
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
@ -1,5 +1,6 @@
|
|||||||
---
|
---
|
||||||
language: Cpp
|
language: Cpp
|
||||||
BasedOnStyle: Google
|
BasedOnStyle: Google
|
||||||
|
ColumnLimit: 120
|
||||||
IndentWidth: 4
|
IndentWidth: 4
|
||||||
---
|
---
|
||||||
|
|||||||
1
ESP/.gitignore
vendored
1
ESP/.gitignore
vendored
@ -2,3 +2,4 @@
|
|||||||
.vscode/
|
.vscode/
|
||||||
build/
|
build/
|
||||||
*.log
|
*.log
|
||||||
|
/tools/firmware_name.txt
|
||||||
|
|||||||
@ -42,7 +42,7 @@ void ProjectConfig::initConfig()
|
|||||||
}
|
}
|
||||||
this->config.mdns = {
|
this->config.mdns = {
|
||||||
_mdnsName,
|
_mdnsName,
|
||||||
"",
|
"openiristracker",
|
||||||
};
|
};
|
||||||
|
|
||||||
log_i("MDNS name: %s", _mdnsName.c_str());
|
log_i("MDNS name: %s", _mdnsName.c_str());
|
||||||
@ -223,11 +223,16 @@ void ProjectConfig::load()
|
|||||||
//! DeviceConfig
|
//! DeviceConfig
|
||||||
//*
|
//*
|
||||||
//**********************************************************************************************************************
|
//**********************************************************************************************************************
|
||||||
void ProjectConfig::setDeviceConfig(const std::string &OTAPassword, int *OTAPort, bool shouldNotify)
|
void ProjectConfig::setDeviceConfig(const std::string &OTAPassword, int OTAPort, const std::string &binaryName, bool shouldNotify)
|
||||||
{
|
{
|
||||||
log_d("Updating device config");
|
log_d("Updating device config");
|
||||||
this->config.device.OTAPassword.assign(OTAPassword);
|
this->config.device.OTAPassword.assign(OTAPassword);
|
||||||
this->config.device.OTAPort = *OTAPort;
|
this->config.device.OTAPort = OTAPort;
|
||||||
|
// check if binary name is empty
|
||||||
|
if (binaryName.empty())
|
||||||
|
log_w("Binary name is empty, using previous value");
|
||||||
|
else
|
||||||
|
this->config.device.binaryName.assign(binaryName);
|
||||||
|
|
||||||
if (shouldNotify)
|
if (shouldNotify)
|
||||||
this->notify(ObserverEvent::deviceConfigUpdated);
|
this->notify(ObserverEvent::deviceConfigUpdated);
|
||||||
|
|||||||
@ -29,6 +29,7 @@ public:
|
|||||||
{
|
{
|
||||||
std::string OTAPassword;
|
std::string OTAPassword;
|
||||||
int OTAPort;
|
int OTAPort;
|
||||||
|
std::string binaryName;
|
||||||
std::string toRepresentation();
|
std::string toRepresentation();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -106,7 +107,7 @@ public:
|
|||||||
MDNSConfig_t *getMDNSConfig();
|
MDNSConfig_t *getMDNSConfig();
|
||||||
WiFiTxPower_t *getWiFiTxPowerConfig();
|
WiFiTxPower_t *getWiFiTxPowerConfig();
|
||||||
|
|
||||||
void setDeviceConfig(const std::string &OTAPassword, int *OTAPort, bool shouldNotify);
|
void setDeviceConfig(const std::string &OTAPassword, int OTAPort, const std::string &binaryName, bool shouldNotify);
|
||||||
void setMDNSConfig(const std::string &hostname, const std::string &service, bool shouldNotify);
|
void setMDNSConfig(const std::string &hostname, const std::string &service, bool shouldNotify);
|
||||||
void setCameraConfig(uint8_t *vflip, uint8_t *framesize, uint8_t *href, uint8_t *quality, uint8_t *brightness, bool shouldNotify);
|
void setCameraConfig(uint8_t *vflip, uint8_t *framesize, uint8_t *href, uint8_t *quality, uint8_t *brightness, bool shouldNotify);
|
||||||
void setWifiConfig(const std::string &networkName, const std::string &ssid, const std::string &password, uint8_t *channel, uint8_t *power, bool adhoc, bool shouldNotify);
|
void setWifiConfig(const std::string &networkName, const std::string &ssid, const std::string &password, uint8_t *channel, uint8_t *power, bool adhoc, bool shouldNotify);
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
#include "serialmanager.hpp"
|
/* #include "serialmanager.hpp"
|
||||||
|
|
||||||
std::unordered_map<std::string, SerialManager::Serial_Commands> SerialManager::command_map = {
|
std::unordered_map<std::string, SerialManager::Serial_Commands> SerialManager::command_map = {
|
||||||
{"", NO_INPUT},
|
{"", NO_INPUT},
|
||||||
@ -80,4 +80,4 @@ void SerialManager::handleSerial()
|
|||||||
const char *wifi_config_password = jsonDoc["wifi_config_password"];
|
const char *wifi_config_password = jsonDoc["wifi_config_password"];
|
||||||
const char *wifi_config_channel = jsonDoc["wifi_config_channel"];
|
const char *wifi_config_channel = jsonDoc["wifi_config_channel"];
|
||||||
}
|
}
|
||||||
}
|
} */
|
||||||
@ -50,7 +50,7 @@ void CameraHandler::setupCameraPinout()
|
|||||||
config.pin_sccb_scl = SIOC_GPIO_NUM;
|
config.pin_sccb_scl = SIOC_GPIO_NUM;
|
||||||
config.pin_pwdn = PWDN_GPIO_NUM;
|
config.pin_pwdn = PWDN_GPIO_NUM;
|
||||||
config.pin_reset = RESET_GPIO_NUM;
|
config.pin_reset = RESET_GPIO_NUM;
|
||||||
config.xclk_freq_hz = 16500000; // 10000000 stable,
|
config.xclk_freq_hz = 20000000; // 10000000 stable,
|
||||||
// 16500000 optimal,
|
// 16500000 optimal,
|
||||||
// 20000000 max fps
|
// 20000000 max fps
|
||||||
}
|
}
|
||||||
@ -64,8 +64,13 @@ void CameraHandler::setupBasicResolution()
|
|||||||
{
|
{
|
||||||
log_e("Did not find psram, setting lower image quality");
|
log_e("Did not find psram, setting lower image quality");
|
||||||
config.fb_location = CAMERA_FB_IN_DRAM;
|
config.fb_location = CAMERA_FB_IN_DRAM;
|
||||||
|
<<<<<<< HEAD
|
||||||
config.jpeg_quality = 9;
|
config.jpeg_quality = 9;
|
||||||
config.fb_count = 2;
|
config.fb_count = 2;
|
||||||
|
=======
|
||||||
|
config.jpeg_quality = 1;
|
||||||
|
config.fb_count = 1;
|
||||||
|
>>>>>>> 5e4e1a971c57061f1d3d896b518c2e2828e22500
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -191,4 +196,4 @@ void CameraHandler::resetCamera(bool type)
|
|||||||
Network_Utilities::my_delay(0.05);
|
Network_Utilities::my_delay(0.05);
|
||||||
setupCamera();
|
setupCamera();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
109
ESP/lib/src/network/OTA/Github_OTA/GithubOTAHandler.cpp
Normal file
109
ESP/lib/src/network/OTA/Github_OTA/GithubOTAHandler.cpp
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
#include "GithubOTAHandler.hpp"
|
||||||
|
|
||||||
|
GithubOTAHandler::GithubOTAHandler() {}
|
||||||
|
|
||||||
|
GithubOTAHandler::~GithubOTAHandler() {}
|
||||||
|
|
||||||
|
void GithubOTAHandler::githubOTAEventCallback(void *handler_args, esp_event_base_t base, int32_t id, void *event_data)
|
||||||
|
{
|
||||||
|
Github_OTA_client_handle_t *client = (Github_OTA_client_handle_t *)handler_args;
|
||||||
|
ESP_LOGI(TAG, "Got Update Callback: %s", Github_OTA_get_event_str((Github_OTA_event_e)id));
|
||||||
|
|
||||||
|
if (id == Github_OTA_EVENT_FIRMWARE_UPDATE_PROGRESS)
|
||||||
|
{
|
||||||
|
/* display some progress with the firmware update */
|
||||||
|
ESP_LOGI(TAG, "Firmware Update Progress: %d%%", *((int *)event_data));
|
||||||
|
}
|
||||||
|
(void)client;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GithubOTAHandler::updateFirmware(const std::string &binaryName)
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG, "Starting Github OTA Update Handler");
|
||||||
|
|
||||||
|
esp_err_t ret = nvs_flash_init();
|
||||||
|
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
|
||||||
|
{
|
||||||
|
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||||
|
ret = nvs_flash_init();
|
||||||
|
}
|
||||||
|
ESP_ERROR_CHECK(ret);
|
||||||
|
|
||||||
|
// initialize our Github_OTA config
|
||||||
|
Github_OTA_config_t ghconfig = {
|
||||||
|
.filenamematch = (char *)binaryName.c_str(),
|
||||||
|
/* 1 minute as a example, but in production you should pick something larger (remember, Github has ratelimits on the API! )*/
|
||||||
|
.updateInterval = 30,
|
||||||
|
};
|
||||||
|
// initialize Github_OTA
|
||||||
|
Github_OTA_client_handle_t *Github_OTA_client = Github_OTA_init(&ghconfig);
|
||||||
|
if (Github_OTA_client == NULL)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "Github_OTA_client_init failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// register for events relating to the update progress
|
||||||
|
esp_event_handler_register(Github_OTA_EVENTS, ESP_EVENT_ANY_ID, &githubOTAEventCallback, Github_OTA_client);
|
||||||
|
|
||||||
|
#if DO_BACKGROUND_UPDATE
|
||||||
|
/* for private repositories or to get more API calls than anonymous, set a github username and PAT token
|
||||||
|
* see https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token
|
||||||
|
* for more information on how to create a PAT token.
|
||||||
|
*
|
||||||
|
* Be carefull, as the PAT token will be stored in your firmware etc and can be used to access your github account.
|
||||||
|
*/
|
||||||
|
// ESP_ERROR_CHECK(Github_OTA_set_auth(Github_OTA_client, "<Insert GH Username>", "<insert PAT TOKEN>"));
|
||||||
|
|
||||||
|
// start a timer that will automatically check for updates based on the interval specified above
|
||||||
|
ESP_ERROR_CHECK(Github_OTA_start_update_timer(Github_OTA_client));
|
||||||
|
|
||||||
|
#elif DO_FORGROUND_UPDATE
|
||||||
|
/* or do a check/update now
|
||||||
|
* This runs in a new task under freeRTOS, so you can do other things while it is running.
|
||||||
|
*/
|
||||||
|
ESP_ERROR_CHECK(Github_OTA_start_update_task(Github_OTA_client));
|
||||||
|
|
||||||
|
#elif DO_MANUAL_CHECK_UPDATE
|
||||||
|
/* Alternatively you can do manual checks
|
||||||
|
* but note, you probably have to increase the Stack size for the task this runs on
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Query the Github Release API for the latest release */
|
||||||
|
ESP_ERROR_CHECK(Github_OTA_check(Github_OTA_client));
|
||||||
|
|
||||||
|
/* get the semver version of the currently running firmware */
|
||||||
|
semver_t *cur = Github_OTA_get_current_version(Github_OTA_client);
|
||||||
|
if (cur)
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG, "Current version: %d.%d.%d", cur->major, cur->minor, cur->patch);
|
||||||
|
semver_free(cur);
|
||||||
|
|
||||||
|
/* get the version of the latest release on Github */
|
||||||
|
semver_t *new = Github_OTA_get_latest_version(Github_OTA_client);
|
||||||
|
if (new)
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG, "New version: %d.%d.%d", new->major, new->minor, new->patch);
|
||||||
|
semver_free(new);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* do some comparisions */
|
||||||
|
if (semver_gt(new, cur) == 1)
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG, "New version is greater than current version");
|
||||||
|
}
|
||||||
|
else if (semver_eq(new, cur) == 1)
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG, "New version is equal to current version");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG, "New version is less than current version");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* assuming we have a new version, then do a actual update */
|
||||||
|
ESP_ERROR_CHECK(Github_OTA_update(Github_OTA_client));
|
||||||
|
/* if there was a new version installed, the esp will reboot after installation and will not reach this code */
|
||||||
|
|
||||||
|
#endif
|
||||||
|
}
|
||||||
30
ESP/lib/src/network/OTA/Github_OTA/GithubOTAHandler.hpp
Normal file
30
ESP/lib/src/network/OTA/Github_OTA/GithubOTAHandler.hpp
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#ifndef GITHUB_OTA_HANDLER_HPP
|
||||||
|
#define GITHUB_OTA_HANDLER_HPP
|
||||||
|
// #include <Arduino.h>
|
||||||
|
#include <esp_err.h>
|
||||||
|
#include <esp_log.h>
|
||||||
|
#include "nvs_flash.h"
|
||||||
|
|
||||||
|
// Custom Libraries
|
||||||
|
#include "lib/esp_ota.h"
|
||||||
|
|
||||||
|
//C++ Libraries
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
// ESP && FreeRTOS Libraries
|
||||||
|
#define DO_BACKGROUND_UPDATE 1
|
||||||
|
#define DO_FOREGROUND_UPDATE 0
|
||||||
|
#define DO_MANUAL_CHECK_UPDATE 0
|
||||||
|
|
||||||
|
class GithubOTAHandler
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
public:
|
||||||
|
GithubOTAHandler();
|
||||||
|
~GithubOTAHandler();
|
||||||
|
void updateFirmware(const std::string &binaryName);
|
||||||
|
static void githubOTAEventCallback(void *handler_args, esp_event_base_t base, int32_t id, void *event_data);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // GITHUB_OTA_HANDLER_HPP
|
||||||
829
ESP/lib/src/network/OTA/Github_OTA/lib/LWJSON/lwjson.c
Normal file
829
ESP/lib/src/network/OTA/Github_OTA/lib/LWJSON/lwjson.c
Normal file
@ -0,0 +1,829 @@
|
|||||||
|
#include <string.h>
|
||||||
|
#include "lwjson.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Internal string object
|
||||||
|
*/
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
const char *start; /*!< Original pointer to beginning of JSON object */
|
||||||
|
size_t len; /*!< Total length of input json string */
|
||||||
|
const char *p; /*!< Current char pointer */
|
||||||
|
} lwjson_int_str_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Allocate new token for JSON block
|
||||||
|
* \param[in] lw: LwJSON instance
|
||||||
|
* \return Pointer to new token
|
||||||
|
*/
|
||||||
|
static lwjson_token_t *
|
||||||
|
prv_alloc_token(lwjson_t *lw)
|
||||||
|
{
|
||||||
|
if (lw->next_free_token_pos < lw->tokens_len)
|
||||||
|
{
|
||||||
|
memset(&lw->tokens[lw->next_free_token_pos], 0x00, sizeof(*lw->tokens));
|
||||||
|
return &lw->tokens[lw->next_free_token_pos++];
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Skip all characters that are considered *blank* as per RFC4627
|
||||||
|
* \param[in,out] pobj: Pointer to text that is modified on success
|
||||||
|
* \return \ref lwjsonOK on success, member of \ref lwjsonr_t otherwise
|
||||||
|
*/
|
||||||
|
static lwjsonr_t
|
||||||
|
prv_skip_blank(lwjson_int_str_t *pobj)
|
||||||
|
{
|
||||||
|
while (pobj->p != NULL && *pobj->p != '\0' && (size_t)(pobj->p - pobj->start) < pobj->len)
|
||||||
|
{
|
||||||
|
if (*pobj->p == ' ' || *pobj->p == '\t' || *pobj->p == '\r' || *pobj->p == '\n' || *pobj->p == '\f')
|
||||||
|
{
|
||||||
|
++pobj->p;
|
||||||
|
#if LWJSON_CFG_COMMENTS
|
||||||
|
/* Check for comments and remove them */
|
||||||
|
}
|
||||||
|
else if (*pobj->p == '/')
|
||||||
|
{
|
||||||
|
++pobj->p;
|
||||||
|
if (pobj->p != NULL && *pobj->p == '*')
|
||||||
|
{
|
||||||
|
++pobj->p;
|
||||||
|
while (pobj->p != NULL && *pobj->p != '\0' && (size_t)(pobj->p - pobj->start) < pobj->len)
|
||||||
|
{
|
||||||
|
if (*pobj->p == '*')
|
||||||
|
{
|
||||||
|
++pobj->p;
|
||||||
|
if (*pobj->p == '/')
|
||||||
|
{
|
||||||
|
++pobj->p;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
++pobj->p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* LWJSON_CFG_COMMENTS */
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (pobj->p != NULL && *pobj->p != '\0' && (size_t)(pobj->p - pobj->start) < pobj->len)
|
||||||
|
{
|
||||||
|
return lwjsonOK;
|
||||||
|
}
|
||||||
|
return lwjsonERRJSON;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Parse JSON string that must start end end with double quotes `"` character
|
||||||
|
* It just parses length of characters and does not perform any decode operation
|
||||||
|
* \param[in,out] pobj: Pointer to text that is modified on success
|
||||||
|
* \param[out] pout: Pointer to pointer to string that is set where string starts
|
||||||
|
* \param[out] poutlen: Length of string in units of characters is stored here
|
||||||
|
* \return \ref lwjsonOK on success, member of \ref lwjsonr_t otherwise
|
||||||
|
*/
|
||||||
|
static lwjsonr_t
|
||||||
|
prv_parse_string(lwjson_int_str_t *pobj, const char **pout, size_t *poutlen)
|
||||||
|
{
|
||||||
|
lwjsonr_t res;
|
||||||
|
size_t len = 0;
|
||||||
|
|
||||||
|
if ((res = prv_skip_blank(pobj)) != lwjsonOK)
|
||||||
|
{
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
if (*pobj->p++ != '"')
|
||||||
|
{
|
||||||
|
return lwjsonERRJSON;
|
||||||
|
}
|
||||||
|
*pout = pobj->p;
|
||||||
|
/* Parse string but take care of escape characters */
|
||||||
|
for (;; ++pobj->p, ++len)
|
||||||
|
{
|
||||||
|
if (pobj->p == NULL || *pobj->p == '\0' || (size_t)(pobj->p - pobj->start) >= pobj->len)
|
||||||
|
{
|
||||||
|
return lwjsonERRJSON;
|
||||||
|
}
|
||||||
|
/* Check special characters */
|
||||||
|
if (*pobj->p == '\\')
|
||||||
|
{
|
||||||
|
++pobj->p;
|
||||||
|
++len;
|
||||||
|
switch (*pobj->p)
|
||||||
|
{
|
||||||
|
case '"':
|
||||||
|
case '\\':
|
||||||
|
case '/':
|
||||||
|
case 'b':
|
||||||
|
case 'f':
|
||||||
|
case 'n':
|
||||||
|
case 'r':
|
||||||
|
case 't':
|
||||||
|
break;
|
||||||
|
case 'u':
|
||||||
|
++pobj->p;
|
||||||
|
for (size_t i = 0; i < 4; ++i, ++len)
|
||||||
|
{
|
||||||
|
if (!((*pobj->p >= '0' && *pobj->p <= '9') || (*pobj->p >= 'a' && *pobj->p <= 'f') || (*pobj->p >= 'A' && *pobj->p <= 'F')))
|
||||||
|
{
|
||||||
|
return lwjsonERRJSON;
|
||||||
|
}
|
||||||
|
if (i < 3)
|
||||||
|
{
|
||||||
|
++pobj->p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return lwjsonERRJSON;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (*pobj->p == '"')
|
||||||
|
{
|
||||||
|
++pobj->p;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*poutlen = len;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Parse property name that must comply with JSON string format as in RFC4627
|
||||||
|
* Property string must be followed by colon character ":"
|
||||||
|
* \param[in,out] pobj: Pointer to text that is modified on success
|
||||||
|
* \param[out] t: Token instance to write property name to
|
||||||
|
* \return \ref lwjsonOK on success, member of \ref lwjsonr_t otherwise
|
||||||
|
*/
|
||||||
|
static lwjsonr_t
|
||||||
|
prv_parse_property_name(lwjson_int_str_t *pobj, lwjson_token_t *t)
|
||||||
|
{
|
||||||
|
lwjsonr_t res;
|
||||||
|
|
||||||
|
/* Parse property string first */
|
||||||
|
if ((res = prv_parse_string(pobj, &t->token_name, &t->token_name_len)) != lwjsonOK)
|
||||||
|
{
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
/* Skip any spaces */
|
||||||
|
if ((res = prv_skip_blank(pobj)) != lwjsonOK)
|
||||||
|
{
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
/* Must continue with colon */
|
||||||
|
if (*pobj->p++ != ':')
|
||||||
|
{
|
||||||
|
return lwjsonERRJSON;
|
||||||
|
}
|
||||||
|
/* Skip any spaces */
|
||||||
|
if ((res = prv_skip_blank(pobj)) != lwjsonOK)
|
||||||
|
{
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
return lwjsonOK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Parse number as described in RFC4627
|
||||||
|
* \param[in,out] pobj: Pointer to text that is modified on success
|
||||||
|
* \param[out] tout: Pointer to output number format
|
||||||
|
* \param[out] fout: Pointer to output real-type variable. Used if type is REAL.
|
||||||
|
* \param[out] iout: Pointer to output int-type variable. Used if type is INT.
|
||||||
|
* \return \ref lwjsonOK on success, member of \ref lwjsonr_t otherwise
|
||||||
|
*/
|
||||||
|
static lwjsonr_t
|
||||||
|
prv_parse_number(lwjson_int_str_t *pobj, lwjson_type_t *tout, lwjson_real_t *fout, lwjson_int_t *iout)
|
||||||
|
{
|
||||||
|
lwjsonr_t res;
|
||||||
|
uint8_t is_minus;
|
||||||
|
lwjson_real_t num;
|
||||||
|
lwjson_type_t type = LWJSON_TYPE_NUM_INT;
|
||||||
|
|
||||||
|
if ((res = prv_skip_blank(pobj)) != lwjsonOK)
|
||||||
|
{
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
if (*pobj->p == '\0' || (size_t)(pobj->p - pobj->start) >= pobj->len)
|
||||||
|
{
|
||||||
|
return lwjsonERRJSON;
|
||||||
|
}
|
||||||
|
is_minus = *pobj->p == '-' ? (++pobj->p, 1) : 0;
|
||||||
|
if (*pobj->p == '\0' /* Invalid string */
|
||||||
|
|| *pobj->p < '0' || *pobj->p > '9' /* Character outside number range */
|
||||||
|
|| (*pobj->p == '0' && (pobj->p[1] < '0' && pobj->p[1] > '9')))
|
||||||
|
{ /* Number starts with 0 but not followed by dot */
|
||||||
|
return lwjsonERRJSON;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse number */
|
||||||
|
for (num = 0; *pobj->p >= '0' && *pobj->p <= '9'; ++pobj->p)
|
||||||
|
{
|
||||||
|
num = num * 10 + (*pobj->p - '0');
|
||||||
|
}
|
||||||
|
if (pobj->p != NULL && *pobj->p == '.')
|
||||||
|
{ /* Number has exponent */
|
||||||
|
lwjson_real_t exp, dec_num;
|
||||||
|
|
||||||
|
type = LWJSON_TYPE_NUM_REAL; /* Format is real */
|
||||||
|
++pobj->p; /* Ignore comma character */
|
||||||
|
if (*pobj->p < '0' || *pobj->p > '9')
|
||||||
|
{ /* Must be followed by number characters */
|
||||||
|
return lwjsonERRJSON;
|
||||||
|
}
|
||||||
|
/* Get number after decimal point */
|
||||||
|
for (exp = 1, dec_num = 0; *pobj->p >= '0' && *pobj->p <= '9'; ++pobj->p, exp *= 10)
|
||||||
|
{
|
||||||
|
dec_num = dec_num * 10 + (*pobj->p - '0');
|
||||||
|
}
|
||||||
|
num += dec_num / exp; /* Add decimal part to number */
|
||||||
|
}
|
||||||
|
if (pobj->p != NULL && (*pobj->p == 'e' || *pobj->p == 'E'))
|
||||||
|
{ /* Engineering mode */
|
||||||
|
uint8_t is_minus_exp;
|
||||||
|
int exp_cnt;
|
||||||
|
|
||||||
|
type = LWJSON_TYPE_NUM_REAL; /* Format is real */
|
||||||
|
++pobj->p; /* Ignore enginnering sing part */
|
||||||
|
is_minus_exp = *pobj->p == '-' ? (++pobj->p, 1) : 0; /* Check if negative */
|
||||||
|
if (*pobj->p == '+')
|
||||||
|
{ /* Optional '+' is possible too */
|
||||||
|
++pobj->p;
|
||||||
|
}
|
||||||
|
if (*pobj->p < '0' || *pobj->p > '9')
|
||||||
|
{ /* Must be followed by number characters */
|
||||||
|
return lwjsonERRJSON;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse exponent number */
|
||||||
|
for (exp_cnt = 0; *pobj->p >= '0' && *pobj->p <= '9'; ++pobj->p)
|
||||||
|
{
|
||||||
|
exp_cnt = exp_cnt * 10 + (*pobj->p - '0');
|
||||||
|
}
|
||||||
|
/* Calculate new value for exponent 10^exponent */
|
||||||
|
if (is_minus_exp)
|
||||||
|
{
|
||||||
|
for (; exp_cnt > 0; num /= 10, --exp_cnt)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (; exp_cnt > 0; num *= 10, --exp_cnt)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (is_minus)
|
||||||
|
{
|
||||||
|
num = -num;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write output values */
|
||||||
|
if (tout != NULL)
|
||||||
|
{
|
||||||
|
*tout = type;
|
||||||
|
}
|
||||||
|
if (type == LWJSON_TYPE_NUM_INT)
|
||||||
|
{
|
||||||
|
*iout = (lwjson_int_t)num;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*fout = num;
|
||||||
|
}
|
||||||
|
return lwjsonOK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Create path segment from input path for search operation
|
||||||
|
* \param[in,out] p: Pointer to pointer to input path. Pointer is modified
|
||||||
|
* \param[out] opath: Pointer to pointer to write path segment
|
||||||
|
* \param[out] olen: Pointer to variable to write length of segment
|
||||||
|
* \param[out] is_last: Pointer to write if this is last segment
|
||||||
|
* \return `1` on success, `0` otherwise
|
||||||
|
*/
|
||||||
|
static uint8_t
|
||||||
|
prv_create_path_segment(const char **p, const char **opath, size_t *olen, uint8_t *is_last)
|
||||||
|
{
|
||||||
|
const char *s = *p;
|
||||||
|
|
||||||
|
*is_last = 0;
|
||||||
|
*opath = NULL;
|
||||||
|
*olen = 0;
|
||||||
|
|
||||||
|
/* Check input path */
|
||||||
|
if (s == NULL || *s == '\0')
|
||||||
|
{
|
||||||
|
*is_last = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Path must be one of:
|
||||||
|
* - literal text
|
||||||
|
* - "#" followed by dot "."
|
||||||
|
*/
|
||||||
|
if (*s == '#')
|
||||||
|
{
|
||||||
|
*opath = s;
|
||||||
|
for (*olen = 0;; ++s, ++(*olen))
|
||||||
|
{
|
||||||
|
if (*s == '.')
|
||||||
|
{
|
||||||
|
++s;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (*s == '\0')
|
||||||
|
{
|
||||||
|
if (*olen == 1)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*p = s;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*opath = s;
|
||||||
|
for (*olen = 0; *s != '\0' && *s != '.'; ++(*olen), ++s)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
*p = s + 1;
|
||||||
|
}
|
||||||
|
if (*s == '\0')
|
||||||
|
{
|
||||||
|
*is_last = 1;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Input recursive function for find operation
|
||||||
|
* \param[in] parent: Parent token of type \ref LWJSON_TYPE_ARRAY or LWJSON_TYPE_OBJECT
|
||||||
|
* \param[in] path: Path to search for starting this token further
|
||||||
|
* \return Found token on success, `NULL` otherwise
|
||||||
|
*/
|
||||||
|
static const lwjson_token_t *
|
||||||
|
prv_find(const lwjson_token_t *parent, const char *path)
|
||||||
|
{
|
||||||
|
const char *segment;
|
||||||
|
size_t segment_len;
|
||||||
|
uint8_t is_last, result;
|
||||||
|
|
||||||
|
/* Get path segments */
|
||||||
|
if ((result = prv_create_path_segment(&path, &segment, &segment_len, &is_last)) != 0)
|
||||||
|
{
|
||||||
|
/* Check if detected an array request */
|
||||||
|
if (*segment == '#')
|
||||||
|
{
|
||||||
|
/* Parent must be array */
|
||||||
|
if (parent->type != LWJSON_TYPE_ARRAY)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if index requested */
|
||||||
|
if (segment_len > 1)
|
||||||
|
{
|
||||||
|
const lwjson_token_t *t;
|
||||||
|
size_t index = 0;
|
||||||
|
|
||||||
|
/* Parse number */
|
||||||
|
for (size_t i = 1; i < segment_len; ++i)
|
||||||
|
{
|
||||||
|
if (segment[i] < '0' || segment[i] > '9')
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
index = index * 10 + (segment[i] - '0');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Start from beginning */
|
||||||
|
for (t = parent->u.first_child; t != NULL && index > 0; t = t->next, --index)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
if (t != NULL)
|
||||||
|
{
|
||||||
|
if (is_last)
|
||||||
|
{
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return prv_find(t, path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Scan all indexes and get first match */
|
||||||
|
for (const lwjson_token_t *tmp_t, *t = parent->u.first_child; t != NULL; t = t->next)
|
||||||
|
{
|
||||||
|
if ((tmp_t = prv_find(t, path)) != NULL)
|
||||||
|
{
|
||||||
|
return tmp_t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (parent->type != LWJSON_TYPE_OBJECT)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
for (const lwjson_token_t *t = parent->u.first_child; t != NULL; t = t->next)
|
||||||
|
{
|
||||||
|
if (t->token_name_len == segment_len && !strncmp(t->token_name, segment, segment_len))
|
||||||
|
{
|
||||||
|
const lwjson_token_t *tmp_t;
|
||||||
|
if (is_last)
|
||||||
|
{
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
if ((tmp_t = prv_find(t, path)) != NULL)
|
||||||
|
{
|
||||||
|
return tmp_t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Check for character after opening bracket of array or object
|
||||||
|
* \param[in,out] pobj: JSON string
|
||||||
|
* \param[in] t: Token to check for type
|
||||||
|
* \return \ref lwjsonOK on success, member of \ref lwjsonr_t otherwise
|
||||||
|
*/
|
||||||
|
static inline lwjsonr_t
|
||||||
|
prv_check_valid_char_after_open_bracket(lwjson_int_str_t *pobj, lwjson_token_t *t)
|
||||||
|
{
|
||||||
|
lwjsonr_t res;
|
||||||
|
|
||||||
|
/* Check next character after object open */
|
||||||
|
if ((res = prv_skip_blank(pobj)) != lwjsonOK)
|
||||||
|
{
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
if (*pobj->p == '\0' || (t->type == LWJSON_TYPE_OBJECT && (*pobj->p != '"' && *pobj->p != '}')) || (t->type == LWJSON_TYPE_ARRAY && (*pobj->p != '"' && *pobj->p != ']' && *pobj->p != '[' && *pobj->p != '{' && *pobj->p != '-' && (*pobj->p < '0' || *pobj->p > '9') && *pobj->p != 't' && *pobj->p != 'n' && *pobj->p != 'f')))
|
||||||
|
{
|
||||||
|
res = lwjsonERRJSON;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Setup LwJSON instance for parsing JSON strings
|
||||||
|
* \param[in,out] lw: LwJSON instance
|
||||||
|
* \param[in] tokens: Pointer to array of tokens used for parsing
|
||||||
|
* \param[in] tokens_len: Number of tokens
|
||||||
|
* \return \ref lwjsonOK on success, member of \ref lwjsonr_t otherwise
|
||||||
|
*/
|
||||||
|
lwjsonr_t
|
||||||
|
lwjson_init(lwjson_t *lw, lwjson_token_t *tokens, size_t tokens_len)
|
||||||
|
{
|
||||||
|
memset(lw, 0x00, sizeof(*lw));
|
||||||
|
memset(tokens, 0x00, sizeof(*tokens) * tokens_len);
|
||||||
|
lw->tokens = tokens;
|
||||||
|
lw->tokens_len = tokens_len;
|
||||||
|
lw->first_token.type = LWJSON_TYPE_OBJECT;
|
||||||
|
return lwjsonOK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Parse JSON data with length parameter
|
||||||
|
* JSON format must be complete and must comply with RFC4627
|
||||||
|
* \param[in,out] lw: LwJSON instance
|
||||||
|
* \param[in] json_data: JSON string to parse
|
||||||
|
* \param[in] jsonČlen: JSON data length
|
||||||
|
* \return \ref lwjsonOK on success, member of \ref lwjsonr_t otherwise
|
||||||
|
*/
|
||||||
|
lwjsonr_t
|
||||||
|
lwjson_parse_ex(lwjson_t *lw, const void *json_data, size_t json_len)
|
||||||
|
{
|
||||||
|
lwjsonr_t res = lwjsonOK;
|
||||||
|
lwjson_token_t *t, *to;
|
||||||
|
lwjson_int_str_t pobj = {.start = json_data, .len = json_len, .p = json_data};
|
||||||
|
|
||||||
|
/* Check input parameters */
|
||||||
|
if (lw == NULL || json_data == NULL || json_len == 0)
|
||||||
|
{
|
||||||
|
res = lwjsonERRPAR;
|
||||||
|
goto ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* set first token */
|
||||||
|
to = &lw->first_token;
|
||||||
|
|
||||||
|
/* values from very beginning */
|
||||||
|
lw->flags.parsed = 0;
|
||||||
|
lw->next_free_token_pos = 0;
|
||||||
|
memset(to, 0x00, sizeof(*to));
|
||||||
|
|
||||||
|
/* First parse */
|
||||||
|
if ((res = prv_skip_blank(&pobj)) != lwjsonOK)
|
||||||
|
{
|
||||||
|
goto ret;
|
||||||
|
}
|
||||||
|
if (*pobj.p == '{')
|
||||||
|
{
|
||||||
|
to->type = LWJSON_TYPE_OBJECT;
|
||||||
|
}
|
||||||
|
else if (*pobj.p == '[')
|
||||||
|
{
|
||||||
|
to->type = LWJSON_TYPE_ARRAY;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = lwjsonERRJSON;
|
||||||
|
goto ret;
|
||||||
|
}
|
||||||
|
++pobj.p;
|
||||||
|
if ((res = prv_check_valid_char_after_open_bracket(&pobj, to)) != lwjsonOK)
|
||||||
|
{
|
||||||
|
goto ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Process all characters as indicated by input user */
|
||||||
|
while (pobj.p != NULL && *pobj.p != '\0' && (size_t)(pobj.p - pobj.start) < pobj.len)
|
||||||
|
{
|
||||||
|
/* Filter out blanks */
|
||||||
|
if ((res = prv_skip_blank(&pobj)) != lwjsonOK)
|
||||||
|
{
|
||||||
|
goto ret;
|
||||||
|
}
|
||||||
|
if (*pobj.p == ',')
|
||||||
|
{
|
||||||
|
++pobj.p;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if end of object or array*/
|
||||||
|
if (*pobj.p == (to->type == LWJSON_TYPE_OBJECT ? '}' : ']'))
|
||||||
|
{
|
||||||
|
lwjson_token_t *parent = to->next;
|
||||||
|
to->next = NULL;
|
||||||
|
++pobj.p;
|
||||||
|
|
||||||
|
/* End of string if to == NULL (no parent), check if properly terminated */
|
||||||
|
if ((to = parent) == NULL)
|
||||||
|
{
|
||||||
|
prv_skip_blank(&pobj);
|
||||||
|
res = (pobj.p == NULL || *pobj.p == '\0' || (size_t)(pobj.p - pobj.start) == pobj.len) ? lwjsonOK
|
||||||
|
: lwjsonERR;
|
||||||
|
goto ret;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Allocate new token */
|
||||||
|
if ((t = prv_alloc_token(lw)) == NULL)
|
||||||
|
{
|
||||||
|
res = lwjsonERRMEM;
|
||||||
|
goto ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If object type is not array, first thing is property that starts with quotes */
|
||||||
|
if (to->type != LWJSON_TYPE_ARRAY)
|
||||||
|
{
|
||||||
|
if (*pobj.p != '"')
|
||||||
|
{
|
||||||
|
res = lwjsonERRJSON;
|
||||||
|
goto ret;
|
||||||
|
}
|
||||||
|
if ((res = prv_parse_property_name(&pobj, t)) != lwjsonOK)
|
||||||
|
{
|
||||||
|
goto ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add element to linked list */
|
||||||
|
if (to->u.first_child == NULL)
|
||||||
|
{
|
||||||
|
to->u.first_child = t;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lwjson_token_t *c;
|
||||||
|
for (c = to->u.first_child; c->next != NULL; c = c->next)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
c->next = t;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check next character to process */
|
||||||
|
switch (*pobj.p)
|
||||||
|
{
|
||||||
|
case '{':
|
||||||
|
case '[':
|
||||||
|
t->type = *pobj.p == '{' ? LWJSON_TYPE_OBJECT : LWJSON_TYPE_ARRAY;
|
||||||
|
++pobj.p;
|
||||||
|
if ((res = prv_check_valid_char_after_open_bracket(&pobj, t)) != lwjsonOK)
|
||||||
|
{
|
||||||
|
goto ret;
|
||||||
|
}
|
||||||
|
t->next = to; /* Temporary saved as parent object */
|
||||||
|
to = t;
|
||||||
|
break;
|
||||||
|
case '"':
|
||||||
|
if ((res = prv_parse_string(&pobj, &t->u.str.token_value, &t->u.str.token_value_len)) == lwjsonOK)
|
||||||
|
{
|
||||||
|
t->type = LWJSON_TYPE_STRING;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
goto ret;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
/* RFC4627 is lower-case only */
|
||||||
|
if (strncmp(pobj.p, "true", 4) == 0)
|
||||||
|
{
|
||||||
|
t->type = LWJSON_TYPE_TRUE;
|
||||||
|
pobj.p += 4;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = lwjsonERRJSON;
|
||||||
|
goto ret;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
/* RFC4627 is lower-case only */
|
||||||
|
if (strncmp(pobj.p, "false", 5) == 0)
|
||||||
|
{
|
||||||
|
t->type = LWJSON_TYPE_FALSE;
|
||||||
|
pobj.p += 5;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = lwjsonERRJSON;
|
||||||
|
goto ret;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
/* RFC4627 is lower-case only */
|
||||||
|
if (strncmp(pobj.p, "null", 4) == 0)
|
||||||
|
{
|
||||||
|
t->type = LWJSON_TYPE_NULL;
|
||||||
|
pobj.p += 4;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = lwjsonERRJSON;
|
||||||
|
goto ret;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (*pobj.p == '-' || (*pobj.p >= '0' && *pobj.p <= '9'))
|
||||||
|
{
|
||||||
|
if (prv_parse_number(&pobj, &t->type, &t->u.num_real, &t->u.num_int) != lwjsonOK)
|
||||||
|
{
|
||||||
|
res = lwjsonERRJSON;
|
||||||
|
goto ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = lwjsonERRJSON;
|
||||||
|
goto ret;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Below code is used to check characters after valid tokens */
|
||||||
|
if (t->type == LWJSON_TYPE_ARRAY || t->type == LWJSON_TYPE_OBJECT)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check what are values after the token value
|
||||||
|
*
|
||||||
|
* As per RFC4627, every token value may have one or more
|
||||||
|
* blank characters, followed by one of below options:
|
||||||
|
* - Comma separator for next token
|
||||||
|
* - End of array indication
|
||||||
|
* - End of object indication
|
||||||
|
*/
|
||||||
|
if ((res = prv_skip_blank(&pobj)) != lwjsonOK)
|
||||||
|
{
|
||||||
|
goto ret;
|
||||||
|
}
|
||||||
|
/* Check if valid string is availabe after */
|
||||||
|
if (pobj.p == NULL || *pobj.p == '\0' || (*pobj.p != ',' && *pobj.p != ']' && *pobj.p != '}'))
|
||||||
|
{
|
||||||
|
res = lwjsonERRJSON;
|
||||||
|
goto ret;
|
||||||
|
}
|
||||||
|
else if (*pobj.p == ',')
|
||||||
|
{ /* Check to advance to next token immediatey */
|
||||||
|
++pobj.p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (to != &lw->first_token || (to != NULL && to->next != NULL))
|
||||||
|
{
|
||||||
|
res = lwjsonERRJSON;
|
||||||
|
to = NULL;
|
||||||
|
}
|
||||||
|
if (to != NULL)
|
||||||
|
{
|
||||||
|
if (to->type != LWJSON_TYPE_ARRAY && to->type != LWJSON_TYPE_OBJECT)
|
||||||
|
{
|
||||||
|
res = lwjsonERRJSON;
|
||||||
|
}
|
||||||
|
to->token_name = NULL;
|
||||||
|
to->token_name_len = 0;
|
||||||
|
}
|
||||||
|
ret:
|
||||||
|
if (res == lwjsonOK)
|
||||||
|
{
|
||||||
|
lw->flags.parsed = 1;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Parse input JSON format
|
||||||
|
* JSON format must be complete and must comply with RFC4627
|
||||||
|
* \param[in,out] lw: LwJSON instance
|
||||||
|
* \param[in] json_str: JSON string to parse
|
||||||
|
* \return \ref lwjsonOK on success, member of \ref lwjsonr_t otherwise
|
||||||
|
*/
|
||||||
|
lwjsonr_t
|
||||||
|
lwjson_parse(lwjson_t *lw, const char *json_str)
|
||||||
|
{
|
||||||
|
return lwjson_parse_ex(lw, json_str, strlen(json_str));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Free token instances (specially used in case of dynamic memory allocation)
|
||||||
|
* \param[in,out] lw: LwJSON instance
|
||||||
|
* \return \ref lwjsonOK on success, member of \ref lwjsonr_t otherwise
|
||||||
|
*/
|
||||||
|
lwjsonr_t
|
||||||
|
lwjson_free(lwjson_t *lw)
|
||||||
|
{
|
||||||
|
memset(lw->tokens, 0x00, sizeof(*lw->tokens) * lw->tokens_len);
|
||||||
|
lw->flags.parsed = 0;
|
||||||
|
return lwjsonOK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Find first match in the given path for JSON entry
|
||||||
|
* JSON must be valid and parsed with \ref lwjson_parse function
|
||||||
|
* \param[in] lw: JSON instance with parsed JSON string
|
||||||
|
* \param[in] path: Path with dot-separated entries to search for the JSON key to return
|
||||||
|
* \return Pointer to found token on success, `NULL` if token cannot be found
|
||||||
|
*/
|
||||||
|
const lwjson_token_t *
|
||||||
|
lwjson_find(lwjson_t *lw, const char *path)
|
||||||
|
{
|
||||||
|
if (lw == NULL || !lw->flags.parsed || path == NULL)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return prv_find(lwjson_get_first_token(lw), path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Find first match in the given path for JSON path
|
||||||
|
* JSON must be valid and parsed with \ref lwjson_parse function
|
||||||
|
*
|
||||||
|
* \param[in] lw: JSON instance with parsed JSON string
|
||||||
|
* \param[in] token: Root token to start search at.
|
||||||
|
* Token must be type \ref LWJSON_TYPE_OBJECT or \ref LWJSON_TYPE_ARRAY.
|
||||||
|
* Set to `NULL` to use root token of LwJSON object
|
||||||
|
* \param[in] path: path with dot-separated entries to search for JSON key
|
||||||
|
* \return Pointer to found token on success, `NULL` if token cannot be found
|
||||||
|
*/
|
||||||
|
const lwjson_token_t *
|
||||||
|
lwjson_find_ex(lwjson_t *lw, const lwjson_token_t *token, const char *path)
|
||||||
|
{
|
||||||
|
if (lw == NULL || !lw->flags.parsed || path == NULL)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (token == NULL)
|
||||||
|
{
|
||||||
|
token = lwjson_get_first_token(lw);
|
||||||
|
}
|
||||||
|
if (token == NULL || (token->type != LWJSON_TYPE_ARRAY && token->type != LWJSON_TYPE_OBJECT))
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return prv_find(token, path);
|
||||||
|
}
|
||||||
356
ESP/lib/src/network/OTA/Github_OTA/lib/LWJSON/lwjson.h
Normal file
356
ESP/lib/src/network/OTA/Github_OTA/lib/LWJSON/lwjson.h
Normal file
@ -0,0 +1,356 @@
|
|||||||
|
/**
|
||||||
|
* \file lwjson.h
|
||||||
|
* \brief LwJSON - Lightweight JSON format parser
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 Tilen MAJERLE
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* This file is part of LwJSON - Lightweight JSON format parser.
|
||||||
|
*
|
||||||
|
* Author: Tilen MAJERLE <tilen@majerle.eu>
|
||||||
|
* Version: v1.5.0
|
||||||
|
*/
|
||||||
|
#ifndef LWJSON_HDR_H
|
||||||
|
#define LWJSON_HDR_H
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "lwjson_opt.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif /* __cplusplus */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \defgroup LWJSON Lightweight JSON format parser
|
||||||
|
* \brief LwJSON - Lightweight JSON format parser
|
||||||
|
* \{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Get size of statically allocated array
|
||||||
|
* \param[in] x: Object to get array size of
|
||||||
|
* \return Number of elements in array
|
||||||
|
*/
|
||||||
|
#define LWJSON_ARRAYSIZE(x) (sizeof(x) / sizeof((x)[0]))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief List of supported JSON types
|
||||||
|
*/
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
LWJSON_TYPE_STRING, /*!< String/Text format. Everything that has beginning and ending quote character */
|
||||||
|
LWJSON_TYPE_NUM_INT, /*!< Number type for integer */
|
||||||
|
LWJSON_TYPE_NUM_REAL, /*!< Number type for real number */
|
||||||
|
LWJSON_TYPE_OBJECT, /*!< Object data type */
|
||||||
|
LWJSON_TYPE_ARRAY, /*!< Array data type */
|
||||||
|
LWJSON_TYPE_TRUE, /*!< True boolean value */
|
||||||
|
LWJSON_TYPE_FALSE, /*!< False boolean value */
|
||||||
|
LWJSON_TYPE_NULL, /*!< Null value */
|
||||||
|
} lwjson_type_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Real data type
|
||||||
|
*/
|
||||||
|
typedef LWJSON_CFG_REAL_TYPE lwjson_real_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Integer data type
|
||||||
|
*/
|
||||||
|
typedef LWJSON_CFG_INT_TYPE lwjson_int_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief JSON token
|
||||||
|
*/
|
||||||
|
typedef struct lwjson_token
|
||||||
|
{
|
||||||
|
struct lwjson_token *next; /*!< Next token on a list */
|
||||||
|
lwjson_type_t type; /*!< Token type */
|
||||||
|
const char *token_name; /*!< Token name (if exists) */
|
||||||
|
size_t token_name_len; /*!< Length of token name (this is needed to support const input strings to parse) */
|
||||||
|
|
||||||
|
union
|
||||||
|
{
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
const char *token_value; /*!< Pointer to the beginning of the string */
|
||||||
|
size_t
|
||||||
|
token_value_len; /*!< Length of token value (this is needed to support const input strings to parse) */
|
||||||
|
} str; /*!< String data */
|
||||||
|
|
||||||
|
lwjson_real_t num_real; /*!< Real number format */
|
||||||
|
lwjson_int_t num_int; /*!< Int number format */
|
||||||
|
struct lwjson_token *first_child; /*!< First children object for object or array type */
|
||||||
|
} u; /*!< Union with different data types */
|
||||||
|
} lwjson_token_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief JSON result enumeration
|
||||||
|
*/
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
lwjsonOK = 0x00, /*!< Function returns successfully */
|
||||||
|
lwjsonERR, /*!< Generic error message */
|
||||||
|
lwjsonERRJSON, /*!< Error JSON format */
|
||||||
|
lwjsonERRMEM, /*!< Memory error */
|
||||||
|
lwjsonERRPAR, /*!< Parameter error */
|
||||||
|
|
||||||
|
lwjsonSTREAMWAITFIRSTCHAR, /*!< Streaming parser did not yet receive first valid character
|
||||||
|
indicating start of JSON sequence */
|
||||||
|
lwjsonSTREAMDONE, /*!< Streaming parser is done,
|
||||||
|
closing character matched the stream opening one */
|
||||||
|
lwjsonSTREAMINPROG, /*!< Stream parsing is still in progress */
|
||||||
|
}
|
||||||
|
|
||||||
|
lwjsonr_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief LwJSON instance
|
||||||
|
*/
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
lwjson_token_t *tokens; /*!< Pointer to array of tokens */
|
||||||
|
size_t tokens_len; /*!< Size of all tokens */
|
||||||
|
size_t next_free_token_pos; /*!< Position of next free token instance */
|
||||||
|
lwjson_token_t first_token; /*!< First token on a list */
|
||||||
|
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
uint8_t parsed : 1; /*!< Flag indicating JSON parsing has finished successfully */
|
||||||
|
} flags; /*!< List of flags */
|
||||||
|
} lwjson_t;
|
||||||
|
|
||||||
|
lwjsonr_t lwjson_init(lwjson_t *lw, lwjson_token_t *tokens, size_t tokens_len);
|
||||||
|
lwjsonr_t lwjson_parse_ex(lwjson_t *lw, const void *json_data, size_t len);
|
||||||
|
lwjsonr_t lwjson_parse(lwjson_t *lw, const char *json_str);
|
||||||
|
const lwjson_token_t *lwjson_find(lwjson_t *lw, const char *path);
|
||||||
|
const lwjson_token_t *lwjson_find_ex(lwjson_t *lw, const lwjson_token_t *token, const char *path);
|
||||||
|
lwjsonr_t lwjson_free(lwjson_t *lw);
|
||||||
|
|
||||||
|
void lwjson_print_token(const lwjson_token_t *token);
|
||||||
|
void lwjson_print_json(const lwjson_t *lw);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Object type for streaming parser
|
||||||
|
*/
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
LWJSON_STREAM_TYPE_NONE, /*!< No entry - not used */
|
||||||
|
LWJSON_STREAM_TYPE_OBJECT, /*!< Object indication */
|
||||||
|
LWJSON_STREAM_TYPE_OBJECT_END, /*!< Object end indication */
|
||||||
|
LWJSON_STREAM_TYPE_ARRAY, /*!< Array indication */
|
||||||
|
LWJSON_STREAM_TYPE_ARRAY_END, /*!< Array end indication */
|
||||||
|
LWJSON_STREAM_TYPE_KEY, /*!< Key string */
|
||||||
|
LWJSON_STREAM_TYPE_STRING, /*!< Strin type */
|
||||||
|
LWJSON_STREAM_TYPE_TRUE, /*!< True primitive */
|
||||||
|
LWJSON_STREAM_TYPE_FALSE, /*!< False primitive */
|
||||||
|
LWJSON_STREAM_TYPE_NULL, /*!< Null primitive */
|
||||||
|
LWJSON_STREAM_TYPE_NUMBER, /*!< Generic number */
|
||||||
|
} lwjson_stream_type_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Stream parsing stack object
|
||||||
|
*/
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
lwjson_stream_type_t type; /*!< Streaming type - current value */
|
||||||
|
|
||||||
|
union
|
||||||
|
{
|
||||||
|
char name[LWJSON_CFG_STREAM_KEY_MAX_LEN + 1]; /*!< Last known key name, used only for \ref LWJSON_STREAM_TYPE_KEY type */
|
||||||
|
uint16_t index; /*!< Current index when type is an array */
|
||||||
|
} meta; /*!< Meta information */
|
||||||
|
} lwjson_stream_stack_t;
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
LWJSON_STREAM_STATE_WAITINGFIRSTCHAR = 0x00, /*!< State to wait for very first opening character */
|
||||||
|
LWJSON_STREAM_STATE_PARSING, /*!< In parsing of the first char state - detecting next character state */
|
||||||
|
LWJSON_STREAM_STATE_PARSING_STRING, /*!< Parse string primitive */
|
||||||
|
LWJSON_STREAM_STATE_PARSING_PRIMITIVE, /*!< Parse any primitive that is non-string, either "true", "false", "null" or a number */
|
||||||
|
} lwjson_stream_state_t;
|
||||||
|
|
||||||
|
/* Forward declaration */
|
||||||
|
struct lwjson_stream_parser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Callback function for various events
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef void (*lwjson_stream_parser_callback_fn)(struct lwjson_stream_parser *jsp, lwjson_stream_type_t type);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief LwJSON streaming structure
|
||||||
|
*/
|
||||||
|
typedef struct lwjson_stream_parser
|
||||||
|
{
|
||||||
|
lwjson_stream_stack_t
|
||||||
|
stack[LWJSON_CFG_STREAM_STACK_SIZE]; /*!< Stack used for parsing. TODO: Add conditional compilation flag */
|
||||||
|
size_t stack_pos; /*!< Current stack position */
|
||||||
|
|
||||||
|
lwjson_stream_state_t parse_state; /*!< Parser state */
|
||||||
|
|
||||||
|
lwjson_stream_parser_callback_fn evt_fn; /*!< Event function for user */
|
||||||
|
|
||||||
|
/* State */
|
||||||
|
union
|
||||||
|
{
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
char buff[LWJSON_CFG_STREAM_STRING_MAX_LEN + 1]; /*!< Buffer to write temporary data. TODO: Size to be variable with define */
|
||||||
|
size_t buff_pos; /*!< Buffer position for next write (length of bytes in buffer) */
|
||||||
|
size_t buff_total_pos; /*!< Total buffer position used up to now (in several data chunks) */
|
||||||
|
uint8_t is_last; /*!< Status indicates if this is the last part of the string */
|
||||||
|
} str; /*!< String structure. It is only used for keys and string objects.
|
||||||
|
Use primitive part for all other options */
|
||||||
|
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
char buff[LWJSON_CFG_STREAM_PRIMITIVE_MAX_LEN + 1]; /*!< Temporary write buffer */
|
||||||
|
size_t buff_pos; /*!< Buffer position for next write */
|
||||||
|
} prim; /*!< Primitive object. Used for all types, except key or string */
|
||||||
|
|
||||||
|
/* Todo: Add other types */
|
||||||
|
} data; /*!< Data union used to parse various */
|
||||||
|
|
||||||
|
char prev_c; /*!< History of characters */
|
||||||
|
void *udata; /*!< User data */
|
||||||
|
} lwjson_stream_parser_t;
|
||||||
|
|
||||||
|
lwjsonr_t lwjson_stream_init(lwjson_stream_parser_t *jsp, lwjson_stream_parser_callback_fn evt_fn);
|
||||||
|
lwjsonr_t lwjson_stream_reset(lwjson_stream_parser_t *jsp);
|
||||||
|
lwjsonr_t lwjson_stream_parse(lwjson_stream_parser_t *jsp, char c);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Get number of tokens used to parse JSON
|
||||||
|
* \param[in] lw: Pointer to LwJSON instance
|
||||||
|
* \return Number of tokens used to parse JSON
|
||||||
|
*/
|
||||||
|
#define lwjson_get_tokens_used(lw) (((lw) != NULL) ? ((lw)->next_free_token_pos + 1) : 0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Get very first token of LwJSON instance
|
||||||
|
* \param[in] lw: Pointer to LwJSON instance
|
||||||
|
* \return Pointer to first token
|
||||||
|
*/
|
||||||
|
#define lwjson_get_first_token(lw) (((lw) != NULL) ? (&(lw)->first_token) : NULL)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Get token value for \ref LWJSON_TYPE_NUM_INT type
|
||||||
|
* \param[in] token: token with integer type
|
||||||
|
* \return Int number if type is integer, `0` otherwise
|
||||||
|
*/
|
||||||
|
#define lwjson_get_val_int(token) \
|
||||||
|
((lwjson_int_t)(((token) != NULL && (token)->type == LWJSON_TYPE_NUM_INT) ? (token)->u.num_int : 0))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Get token value for \ref LWJSON_TYPE_NUM_REAL type
|
||||||
|
* \param[in] token: token with real type
|
||||||
|
* \return Real numbeer if type is real, `0` otherwise
|
||||||
|
*/
|
||||||
|
#define lwjson_get_val_real(token) \
|
||||||
|
((lwjson_real_t)(((token) != NULL && (token)->type == LWJSON_TYPE_NUM_REAL) ? (token)->u.num_real : 0))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Get first child token for \ref LWJSON_TYPE_OBJECT or \ref LWJSON_TYPE_ARRAY types
|
||||||
|
* \param[in] token: token with integer type
|
||||||
|
* \return Pointer to first child or `NULL` if parent token is not object or array
|
||||||
|
*/
|
||||||
|
#define lwjson_get_first_child(token) \
|
||||||
|
(const void *)(((token) != NULL && ((token)->type == LWJSON_TYPE_OBJECT || (token)->type == LWJSON_TYPE_ARRAY)) \
|
||||||
|
? (token)->u.first_child \
|
||||||
|
: NULL)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Get string value from JSON token
|
||||||
|
* \param[in] token: Token with string type
|
||||||
|
* \param[out] str_len: Pointer to variable holding length of string.
|
||||||
|
* Set to `NULL` if not used
|
||||||
|
* \return Pointer to string or `NULL` if invalid token type
|
||||||
|
*/
|
||||||
|
static inline const char *
|
||||||
|
lwjson_get_val_string(const lwjson_token_t *token, size_t *str_len)
|
||||||
|
{
|
||||||
|
if (token != NULL && token->type == LWJSON_TYPE_STRING)
|
||||||
|
{
|
||||||
|
if (str_len != NULL)
|
||||||
|
{
|
||||||
|
*str_len = token->u.str.token_value_len;
|
||||||
|
}
|
||||||
|
return token->u.str.token_value;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Get length of string for \ref LWJSON_TYPE_STRING token type
|
||||||
|
* \param[in] token: token with string type
|
||||||
|
* \return Length of string in units of bytes
|
||||||
|
*/
|
||||||
|
#define lwjson_get_val_string_length(token) \
|
||||||
|
((size_t)(((token) != NULL && (token)->type == LWJSON_TYPE_STRING) ? (token)->u.str.token_value_len : 0))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Compare string token with user input string for a case-sensitive match
|
||||||
|
* \param[in] token: Token with string type
|
||||||
|
* \param[in] str: NULL-terminated string to compare
|
||||||
|
* \return `1` if equal, `0` otherwise
|
||||||
|
*/
|
||||||
|
static inline uint8_t
|
||||||
|
lwjson_string_compare(const lwjson_token_t *token, const char *str)
|
||||||
|
{
|
||||||
|
if (token != NULL && token->type == LWJSON_TYPE_STRING)
|
||||||
|
{
|
||||||
|
return strncmp(token->u.str.token_value, str, token->u.str.token_value_len) == 0;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Compare string token with user input string for a case-sensitive match
|
||||||
|
* \param[in] token: Token with string type
|
||||||
|
* \param[in] str: NULL-terminated string to compare
|
||||||
|
* \param[in] len: Length of the string in bytes
|
||||||
|
* \return `1` if equal, `0` otherwise
|
||||||
|
*/
|
||||||
|
static inline uint8_t
|
||||||
|
lwjson_string_compare_n(const lwjson_token_t *token, const char *str, size_t len)
|
||||||
|
{
|
||||||
|
if (token != NULL && token->type == LWJSON_TYPE_STRING && len <= token->u.str.token_value_len)
|
||||||
|
{
|
||||||
|
return strncmp(token->u.str.token_value, str, len) == 0;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \}
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif /* __cplusplus */
|
||||||
|
|
||||||
|
#endif /* LWJSON_HDR_H */
|
||||||
149
ESP/lib/src/network/OTA/Github_OTA/lib/LWJSON/lwjson_debug.c
Normal file
149
ESP/lib/src/network/OTA/Github_OTA/lib/LWJSON/lwjson_debug.c
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
/**
|
||||||
|
* \file lwjson_debug.c
|
||||||
|
* \brief Debug and print function for tokens
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 Tilen MAJERLE
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* This file is part of LwJSON - Lightweight JSON format parser.
|
||||||
|
*
|
||||||
|
* Author: Tilen MAJERLE <tilen@majerle.eu>
|
||||||
|
* Version: v1.5.0
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "lwjson.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Token print instance
|
||||||
|
*/
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
size_t indent; /*!< Indent level for token print */
|
||||||
|
} lwjson_token_print_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Print token value
|
||||||
|
* \param[in] p: Token print instance
|
||||||
|
* \param[in] token: Token to print
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
prv_print_token(lwjson_token_print_t *p, const lwjson_token_t *token)
|
||||||
|
{
|
||||||
|
#define print_indent() printf("%.*s", (int)((p->indent)), "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t");
|
||||||
|
|
||||||
|
if (token == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if token has a name */
|
||||||
|
print_indent();
|
||||||
|
if (token->token_name != NULL)
|
||||||
|
{
|
||||||
|
printf("\"%.*s\":", (int)token->token_name_len, token->token_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Print different types */
|
||||||
|
switch (token->type)
|
||||||
|
{
|
||||||
|
case LWJSON_TYPE_OBJECT:
|
||||||
|
case LWJSON_TYPE_ARRAY:
|
||||||
|
{
|
||||||
|
printf("%c", token->type == LWJSON_TYPE_OBJECT ? '{' : '[');
|
||||||
|
if (token->u.first_child != NULL)
|
||||||
|
{
|
||||||
|
printf("\n");
|
||||||
|
++p->indent;
|
||||||
|
for (const lwjson_token_t *t = lwjson_get_first_child(token); t != NULL; t = t->next)
|
||||||
|
{
|
||||||
|
prv_print_token(p, t);
|
||||||
|
}
|
||||||
|
--p->indent;
|
||||||
|
print_indent();
|
||||||
|
}
|
||||||
|
printf("%c", token->type == LWJSON_TYPE_OBJECT ? '}' : ']');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWJSON_TYPE_STRING:
|
||||||
|
{
|
||||||
|
printf("\"%.*s\"", (int)lwjson_get_val_string_length(token), lwjson_get_val_string(token, NULL));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWJSON_TYPE_NUM_INT:
|
||||||
|
{
|
||||||
|
printf("%lld", (long long)lwjson_get_val_int(token));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWJSON_TYPE_NUM_REAL:
|
||||||
|
{
|
||||||
|
printf("%f", (double)lwjson_get_val_real(token));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWJSON_TYPE_TRUE:
|
||||||
|
{
|
||||||
|
printf("true");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWJSON_TYPE_FALSE:
|
||||||
|
{
|
||||||
|
printf("false");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LWJSON_TYPE_NULL:
|
||||||
|
{
|
||||||
|
printf("NULL");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (token->next != NULL)
|
||||||
|
{
|
||||||
|
printf(",");
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Prints and outputs token data to the stream output
|
||||||
|
* \note This function is not re-entrant
|
||||||
|
* \param[in] token: Token to print
|
||||||
|
*/
|
||||||
|
void lwjson_print_token(const lwjson_token_t *token)
|
||||||
|
{
|
||||||
|
lwjson_token_print_t p = {0};
|
||||||
|
prv_print_token(&p, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Prints and outputs full parsed LwJSON instance
|
||||||
|
* \note This function is not re-entrant
|
||||||
|
* \param[in] lw: LwJSON instance to print
|
||||||
|
*/
|
||||||
|
void lwjson_print_json(const lwjson_t *lw)
|
||||||
|
{
|
||||||
|
lwjson_token_print_t p = {0};
|
||||||
|
prv_print_token(&p, lwjson_get_first_token(lw));
|
||||||
|
}
|
||||||
137
ESP/lib/src/network/OTA/Github_OTA/lib/LWJSON/lwjson_opt.h
Normal file
137
ESP/lib/src/network/OTA/Github_OTA/lib/LWJSON/lwjson_opt.h
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
/**
|
||||||
|
* \file lwjson_opt.h
|
||||||
|
* \brief LwJSON options
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 Tilen MAJERLE
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* This file is part of LwJSON - Lightweight JSON format parser.
|
||||||
|
*
|
||||||
|
* Author: Tilen MAJERLE <tilen@majerle.eu>
|
||||||
|
* Version: v1.5.0
|
||||||
|
*/
|
||||||
|
#ifndef LWJSON_HDR_OPT_H
|
||||||
|
#define LWJSON_HDR_OPT_H
|
||||||
|
|
||||||
|
// #define LWJSON_DEV 1
|
||||||
|
/* Uncomment to ignore user options (or set macro in compiler flags) */
|
||||||
|
#define LWJSON_IGNORE_USER_OPTS
|
||||||
|
|
||||||
|
/* Include application options */
|
||||||
|
#ifndef LWJSON_IGNORE_USER_OPTS
|
||||||
|
#include "lwjson_opts.h"
|
||||||
|
#endif /* LWJSON_IGNORE_USER_OPTS */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif /* __cplusplus */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \defgroup LWJSON_OPT Configuration
|
||||||
|
* \brief LwJSON options
|
||||||
|
* \{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Real data type used to parse numbers with floating point number
|
||||||
|
* \note Data type must be signed, normally `float` or `double`
|
||||||
|
*
|
||||||
|
* This is used for numbers in \ref LWJSON_TYPE_NUM_REAL token data type.
|
||||||
|
*/
|
||||||
|
#ifndef LWJSON_CFG_REAL_TYPE
|
||||||
|
#define LWJSON_CFG_REAL_TYPE float
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Integer type used to parse numbers
|
||||||
|
* \note Data type must be signed integer
|
||||||
|
*
|
||||||
|
* This is used for numbers in \ref LWJSON_TYPE_NUM_INT token data type.
|
||||||
|
*/
|
||||||
|
#ifndef LWJSON_CFG_INT_TYPE
|
||||||
|
#define LWJSON_CFG_INT_TYPE long long
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Enables `1` or disables `0` support for inline comments
|
||||||
|
*
|
||||||
|
* Default set to `0` to be JSON compliant
|
||||||
|
*/
|
||||||
|
#ifndef LWJSON_CFG_COMMENTS
|
||||||
|
#define LWJSON_CFG_COMMENTS 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \defgroup LWJSON_OPT_STREAM JSON stream
|
||||||
|
* \brief JSON streaming confiuration
|
||||||
|
* \{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Max length of token key (object key name) to be available for stack storage
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef LWJSON_CFG_STREAM_KEY_MAX_LEN
|
||||||
|
#define LWJSON_CFG_STREAM_KEY_MAX_LEN 32
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Max stack size (depth) in units of \ref lwjson_stream_stack_t structure
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef LWJSON_CFG_STREAM_STACK_SIZE
|
||||||
|
#define LWJSON_CFG_STREAM_STACK_SIZE 16
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Max size of string for single parsing in units of bytes
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef LWJSON_CFG_STREAM_STRING_MAX_LEN
|
||||||
|
#define LWJSON_CFG_STREAM_STRING_MAX_LEN 256
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Max number of bytes used to parse primitive.
|
||||||
|
*
|
||||||
|
* Primitives are all numbers and logical values (null, true, false)
|
||||||
|
*/
|
||||||
|
#ifndef LWJSON_CFG_STREAM_PRIMITIVE_MAX_LEN
|
||||||
|
#define LWJSON_CFG_STREAM_PRIMITIVE_MAX_LEN 32
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \}
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif /* __cplusplus */
|
||||||
|
|
||||||
|
#endif /* LWJSON_HDR_OPT_H */
|
||||||
514
ESP/lib/src/network/OTA/Github_OTA/lib/LWJSON/lwjson_stream.c
Normal file
514
ESP/lib/src/network/OTA/Github_OTA/lib/LWJSON/lwjson_stream.c
Normal file
@ -0,0 +1,514 @@
|
|||||||
|
/**
|
||||||
|
* \file lwjson_stream.c
|
||||||
|
* \brief Lightweight JSON format parser
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 Tilen MAJERLE
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* This file is part of LwJSON - Lightweight JSON format parser.
|
||||||
|
*
|
||||||
|
* Author: Tilen MAJERLE <tilen@majerle.eu>
|
||||||
|
* Version: v1.5.0
|
||||||
|
*/
|
||||||
|
#include <string.h>
|
||||||
|
#include "lwjson.h"
|
||||||
|
|
||||||
|
#if defined(LWJSON_DEV)
|
||||||
|
#include <stdio.h>
|
||||||
|
#define DEBUG_STRING_PREFIX_SPACES \
|
||||||
|
" "
|
||||||
|
#define LWJSON_DEBUG(jsp, ...) \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
if ((jsp) != NULL) \
|
||||||
|
{ \
|
||||||
|
printf("%.*s", (int)(4 * (jsp)->stack_pos), DEBUG_STRING_PREFIX_SPACES); \
|
||||||
|
} \
|
||||||
|
printf(__VA_ARGS__); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/* Strings for debug */
|
||||||
|
static const char *type_strings[] = {
|
||||||
|
[LWJSON_STREAM_TYPE_NONE] = "none",
|
||||||
|
[LWJSON_STREAM_TYPE_OBJECT] = "object",
|
||||||
|
[LWJSON_STREAM_TYPE_OBJECT_END] = "object_end",
|
||||||
|
[LWJSON_STREAM_TYPE_ARRAY] = "array",
|
||||||
|
[LWJSON_STREAM_TYPE_ARRAY_END] = "array_end",
|
||||||
|
[LWJSON_STREAM_TYPE_KEY] = "key",
|
||||||
|
[LWJSON_STREAM_TYPE_STRING] = "string",
|
||||||
|
[LWJSON_STREAM_TYPE_TRUE] = "true",
|
||||||
|
[LWJSON_STREAM_TYPE_FALSE] = "false",
|
||||||
|
[LWJSON_STREAM_TYPE_NULL] = "null",
|
||||||
|
[LWJSON_STREAM_TYPE_NUMBER] = "number",
|
||||||
|
};
|
||||||
|
#else
|
||||||
|
#define LWJSON_DEBUG(jsp, ...)
|
||||||
|
#endif /* defined(LWJSON_DEV) */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Sends an event to user for further processing
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#define SEND_EVT(jsp, type) \
|
||||||
|
if ((jsp) != NULL && (jsp)->evt_fn != NULL) \
|
||||||
|
{ \
|
||||||
|
(jsp)->evt_fn((jsp), (type)); \
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Check if character is a space character (with extended chars)
|
||||||
|
* \param[in] c: Character to check
|
||||||
|
* \return `1` if considered extended space, `0` otherwise
|
||||||
|
*/
|
||||||
|
#define prv_is_space_char_ext(c) ((c) == ' ' || (c) == '\t' || (c) == '\r' || (c) == '\n' || (c) == '\f')
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Push "parent" state to the artificial stack
|
||||||
|
* \param jsp: JSON stream parser instance
|
||||||
|
* \param type: Stream type to be pushed on stack
|
||||||
|
* \return `1` on success, `0` otherwise
|
||||||
|
*/
|
||||||
|
static uint8_t
|
||||||
|
prv_stack_push(lwjson_stream_parser_t *jsp, lwjson_stream_type_t type)
|
||||||
|
{
|
||||||
|
if (jsp->stack_pos < LWJSON_ARRAYSIZE(jsp->stack))
|
||||||
|
{
|
||||||
|
jsp->stack[jsp->stack_pos].type = type;
|
||||||
|
jsp->stack[jsp->stack_pos].meta.index = 0;
|
||||||
|
LWJSON_DEBUG(jsp, "Pushed to stack: %s\r\n", type_strings[type]);
|
||||||
|
jsp->stack_pos++;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Pop value from stack (remove it) and return its value
|
||||||
|
* \param jsp: JSON stream parser instance
|
||||||
|
* \return Member of \ref lwjson_stream_type_t enumeration
|
||||||
|
*/
|
||||||
|
static lwjson_stream_type_t
|
||||||
|
prv_stack_pop(lwjson_stream_parser_t *jsp)
|
||||||
|
{
|
||||||
|
if (jsp->stack_pos > 0)
|
||||||
|
{
|
||||||
|
lwjson_stream_type_t t = jsp->stack[--jsp->stack_pos].type;
|
||||||
|
jsp->stack[jsp->stack_pos].type = LWJSON_STREAM_TYPE_NONE;
|
||||||
|
LWJSON_DEBUG(jsp, "Popped from stack: %s\r\n", type_strings[t]);
|
||||||
|
|
||||||
|
/* Take care of array to indicate number of entries */
|
||||||
|
if (jsp->stack_pos > 0 && jsp->stack[jsp->stack_pos - 1].type == LWJSON_STREAM_TYPE_ARRAY)
|
||||||
|
{
|
||||||
|
jsp->stack[jsp->stack_pos - 1].meta.index++;
|
||||||
|
}
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
return LWJSON_STREAM_TYPE_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Get top type value currently on the stack
|
||||||
|
* \param jsp: JSON stream parser instance
|
||||||
|
* \return Member of \ref lwjson_stream_type_t enumeration
|
||||||
|
*/
|
||||||
|
static lwjson_stream_type_t
|
||||||
|
prv_stack_get_top(lwjson_stream_parser_t *jsp)
|
||||||
|
{
|
||||||
|
if (jsp->stack_pos > 0)
|
||||||
|
{
|
||||||
|
return jsp->stack[jsp->stack_pos - 1].type;
|
||||||
|
}
|
||||||
|
return LWJSON_STREAM_TYPE_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Initialize LwJSON stream object before parsing takes place
|
||||||
|
* \param[in,out] jsp: Stream JSON structure
|
||||||
|
* \return \ref lwjsonOK on success, member of \ref lwjsonr_t otherwise
|
||||||
|
*/
|
||||||
|
lwjsonr_t
|
||||||
|
lwjson_stream_init(lwjson_stream_parser_t *jsp, lwjson_stream_parser_callback_fn evt_fn)
|
||||||
|
{
|
||||||
|
memset(jsp, 0x00, sizeof(*jsp));
|
||||||
|
jsp->parse_state = LWJSON_STREAM_STATE_WAITINGFIRSTCHAR;
|
||||||
|
jsp->evt_fn = evt_fn;
|
||||||
|
return lwjsonOK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Reset LwJSON stream structure
|
||||||
|
*
|
||||||
|
* \param jsp: LwJSON stream parser
|
||||||
|
* \return \ref lwjsonOK on success, member of \ref lwjsonr_t otherwise
|
||||||
|
*/
|
||||||
|
lwjsonr_t
|
||||||
|
lwjson_stream_reset(lwjson_stream_parser_t *jsp)
|
||||||
|
{
|
||||||
|
jsp->parse_state = LWJSON_STREAM_STATE_WAITINGFIRSTCHAR;
|
||||||
|
jsp->stack_pos = 0;
|
||||||
|
return lwjsonOK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Parse JSON string in streaming mode
|
||||||
|
* \param[in,out] jsp: Stream JSON structure
|
||||||
|
* \param[in] c: Character to parse
|
||||||
|
* \return \ref lwjsonOK if parsing is in progress and no hard error detected
|
||||||
|
* \ref lwjsonSTREAMDONE when valid JSON was detected and stack level reached back `0` level
|
||||||
|
*/
|
||||||
|
lwjsonr_t
|
||||||
|
lwjson_stream_parse(lwjson_stream_parser_t *jsp, char c)
|
||||||
|
{
|
||||||
|
/* Get first character first */
|
||||||
|
if (jsp->parse_state == LWJSON_STREAM_STATE_WAITINGFIRSTCHAR && c != '{' && c != '[')
|
||||||
|
{
|
||||||
|
return lwjsonSTREAMDONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
start_over:
|
||||||
|
/*
|
||||||
|
* Determine what to do from parsing state
|
||||||
|
*/
|
||||||
|
switch (jsp->parse_state)
|
||||||
|
{
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Waiting for very first valid characters,
|
||||||
|
* that is used to indicate start of JSON stream
|
||||||
|
*/
|
||||||
|
case LWJSON_STREAM_STATE_WAITINGFIRSTCHAR:
|
||||||
|
case LWJSON_STREAM_STATE_PARSING:
|
||||||
|
{
|
||||||
|
/* Determine start of object or an array */
|
||||||
|
if (c == '{' || c == '[')
|
||||||
|
{
|
||||||
|
/* Reset stack pointer if this character came from waiting for first character */
|
||||||
|
if (jsp->parse_state == LWJSON_STREAM_STATE_WAITINGFIRSTCHAR)
|
||||||
|
{
|
||||||
|
jsp->stack_pos = 0;
|
||||||
|
}
|
||||||
|
if (!prv_stack_push(jsp, c == '{' ? LWJSON_STREAM_TYPE_OBJECT : LWJSON_STREAM_TYPE_ARRAY))
|
||||||
|
{
|
||||||
|
LWJSON_DEBUG(jsp, "Cannot push object/array to stack\r\n");
|
||||||
|
return lwjsonERRMEM;
|
||||||
|
}
|
||||||
|
jsp->parse_state = LWJSON_STREAM_STATE_PARSING;
|
||||||
|
SEND_EVT(jsp, c == '{' ? LWJSON_STREAM_TYPE_OBJECT : LWJSON_STREAM_TYPE_ARRAY);
|
||||||
|
|
||||||
|
/* Determine end of object or an array */
|
||||||
|
}
|
||||||
|
else if (c == '}' || c == ']')
|
||||||
|
{
|
||||||
|
lwjson_stream_type_t t = prv_stack_get_top(jsp);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If it is a key last entry on closing area,
|
||||||
|
* it is an error - an example: {"key":}
|
||||||
|
*/
|
||||||
|
if (t == LWJSON_STREAM_TYPE_KEY)
|
||||||
|
{
|
||||||
|
LWJSON_DEBUG(jsp, "ERROR - key should not be followed by ] without value for a key\r\n");
|
||||||
|
return lwjsonERRJSON;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if closing character matches stack value
|
||||||
|
* Avoid cases like: {"key":"value"] or ["v1", "v2", "v3"}
|
||||||
|
*/
|
||||||
|
if ((c == '}' && t != LWJSON_STREAM_TYPE_OBJECT) || (c == ']' && t != LWJSON_STREAM_TYPE_ARRAY))
|
||||||
|
{
|
||||||
|
LWJSON_DEBUG(jsp, "ERROR - closing character '%c' does not match stack element \"%s\"\r\n", c,
|
||||||
|
type_strings[t]);
|
||||||
|
return lwjsonERRJSON;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now remove the array or object from stack */
|
||||||
|
if (prv_stack_pop(jsp) == LWJSON_STREAM_TYPE_NONE)
|
||||||
|
{
|
||||||
|
return lwjsonERRJSON;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if above is a key type
|
||||||
|
* and remove it too as we finished with processing of potential case.
|
||||||
|
*
|
||||||
|
* {"key":{"abc":1}} - remove "key" part
|
||||||
|
*/
|
||||||
|
if (prv_stack_get_top(jsp) == LWJSON_STREAM_TYPE_KEY)
|
||||||
|
{
|
||||||
|
prv_stack_pop(jsp);
|
||||||
|
}
|
||||||
|
SEND_EVT(jsp, c == '}' ? LWJSON_STREAM_TYPE_OBJECT_END : LWJSON_STREAM_TYPE_ARRAY_END);
|
||||||
|
|
||||||
|
/* If that is the end of JSON */
|
||||||
|
if (jsp->stack_pos == 0)
|
||||||
|
{
|
||||||
|
return lwjsonSTREAMDONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Determine start of string - can be key or regular string (in array or after key) */
|
||||||
|
}
|
||||||
|
else if (c == '"')
|
||||||
|
{
|
||||||
|
#if defined(LWJSON_DEV)
|
||||||
|
lwjson_stream_type_t t = prv_stack_get_top(jsp);
|
||||||
|
if (t == LWJSON_STREAM_TYPE_OBJECT)
|
||||||
|
{
|
||||||
|
LWJSON_DEBUG(jsp, "Start of string parsing - expected key name in an object\r\n");
|
||||||
|
}
|
||||||
|
else if (t == LWJSON_STREAM_TYPE_KEY)
|
||||||
|
{
|
||||||
|
LWJSON_DEBUG(jsp,
|
||||||
|
"Start of string parsing - string value associated to previous key in an object\r\n");
|
||||||
|
}
|
||||||
|
else if (t == LWJSON_STREAM_TYPE_ARRAY)
|
||||||
|
{
|
||||||
|
LWJSON_DEBUG(jsp, "Start of string parsing - string entry in an array\r\n");
|
||||||
|
}
|
||||||
|
#endif /* defined(LWJSON_DEV) */
|
||||||
|
jsp->parse_state = LWJSON_STREAM_STATE_PARSING_STRING;
|
||||||
|
memset(&jsp->data.str, 0x00, sizeof(jsp->data.str));
|
||||||
|
|
||||||
|
/* Check for end of key character */
|
||||||
|
}
|
||||||
|
else if (c == ':')
|
||||||
|
{
|
||||||
|
lwjson_stream_type_t t = prv_stack_get_top(jsp);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Color can only be followed by key on the stack
|
||||||
|
*
|
||||||
|
* It is clear JSON error if this is not the case
|
||||||
|
*/
|
||||||
|
if (t != LWJSON_STREAM_TYPE_KEY)
|
||||||
|
{
|
||||||
|
LWJSON_DEBUG(jsp, "Error - wrong ':' character\r\n");
|
||||||
|
return lwjsonERRJSON;
|
||||||
|
}
|
||||||
|
/* Check if this is start of number or "true", "false" or "null" */
|
||||||
|
}
|
||||||
|
else if (c == '-' || (c >= '0' && c <= '9') || c == 't' || c == 'f' || c == 'n')
|
||||||
|
{
|
||||||
|
LWJSON_DEBUG(jsp, "Start of primitive parsing parsing - %s, First char: %c\r\n",
|
||||||
|
(c == '-' || (c >= '0' && c <= '9')) ? "number" : "true,false,null", c);
|
||||||
|
jsp->parse_state = LWJSON_STREAM_STATE_PARSING_PRIMITIVE;
|
||||||
|
memset(&jsp->data.prim, 0x00, sizeof(jsp->data.prim));
|
||||||
|
jsp->data.prim.buff[jsp->data.prim.buff_pos++] = c;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse any type of string in a sequence
|
||||||
|
*
|
||||||
|
* It is used for key or string in an object or an array
|
||||||
|
*/
|
||||||
|
case LWJSON_STREAM_STATE_PARSING_STRING:
|
||||||
|
{
|
||||||
|
lwjson_stream_type_t t = prv_stack_get_top(jsp);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Quote character may trigger end of string,
|
||||||
|
* or if backslasled before - it is part of string
|
||||||
|
*
|
||||||
|
* TODO: Handle backslash
|
||||||
|
*/
|
||||||
|
if (c == '"' && jsp->prev_c != '\\')
|
||||||
|
{
|
||||||
|
#if defined(LWJSON_DEV)
|
||||||
|
if (t == LWJSON_STREAM_TYPE_OBJECT)
|
||||||
|
{
|
||||||
|
LWJSON_DEBUG(jsp, "End of string parsing - object key name: \"%s\"\r\n", jsp->data.str.buff);
|
||||||
|
}
|
||||||
|
else if (t == LWJSON_STREAM_TYPE_KEY)
|
||||||
|
{
|
||||||
|
LWJSON_DEBUG(
|
||||||
|
jsp, "End of string parsing - string value associated to previous key in an object: \"%s\"\r\n",
|
||||||
|
jsp->data.str.buff);
|
||||||
|
}
|
||||||
|
else if (t == LWJSON_STREAM_TYPE_ARRAY)
|
||||||
|
{
|
||||||
|
LWJSON_DEBUG(jsp, "End of string parsing - an array string entry: \"%s\"\r\n", jsp->data.str.buff);
|
||||||
|
}
|
||||||
|
#endif /* defined(LWJSON_DEV) */
|
||||||
|
|
||||||
|
/* Set is_last to 1 as this is the last part of this string token */
|
||||||
|
jsp->data.str.is_last = 1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When top of stack is object - string is treated as a key
|
||||||
|
* When top of stack is a key - string is a value for a key - notify user and pop the value for key
|
||||||
|
* When top of stack is an array - string is one type - notify user and don't do anything
|
||||||
|
*/
|
||||||
|
if (t == LWJSON_STREAM_TYPE_OBJECT)
|
||||||
|
{
|
||||||
|
SEND_EVT(jsp, LWJSON_STREAM_TYPE_KEY);
|
||||||
|
if (prv_stack_push(jsp, LWJSON_STREAM_TYPE_KEY))
|
||||||
|
{
|
||||||
|
size_t len = jsp->data.str.buff_pos;
|
||||||
|
if (len > (sizeof(jsp->stack[0].meta.name) - 1))
|
||||||
|
{
|
||||||
|
len = sizeof(jsp->stack[0].meta.name) - 1;
|
||||||
|
}
|
||||||
|
memcpy(jsp->stack[jsp->stack_pos - 1].meta.name, jsp->data.str.buff, len);
|
||||||
|
jsp->stack[jsp->stack_pos - 1].meta.name[len] = '\0';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LWJSON_DEBUG(jsp, "Cannot push key to stack\r\n");
|
||||||
|
return lwjsonERRMEM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (t == LWJSON_STREAM_TYPE_KEY)
|
||||||
|
{
|
||||||
|
SEND_EVT(jsp, LWJSON_STREAM_TYPE_STRING);
|
||||||
|
prv_stack_pop(jsp);
|
||||||
|
/* Next character to wait for is either space or comma or end of object */
|
||||||
|
}
|
||||||
|
else if (t == LWJSON_STREAM_TYPE_ARRAY)
|
||||||
|
{
|
||||||
|
SEND_EVT(jsp, LWJSON_STREAM_TYPE_STRING);
|
||||||
|
jsp->stack[jsp->stack_pos - 1].meta.index++;
|
||||||
|
}
|
||||||
|
jsp->parse_state = LWJSON_STREAM_STATE_PARSING;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* TODO: Check other backslash elements */
|
||||||
|
jsp->data.str.buff[jsp->data.str.buff_pos++] = c;
|
||||||
|
jsp->data.str.buff_total_pos++;
|
||||||
|
|
||||||
|
/* Handle buffer "overflow" */
|
||||||
|
if (jsp->data.str.buff_pos >= (LWJSON_CFG_STREAM_STRING_MAX_LEN - 1))
|
||||||
|
{
|
||||||
|
jsp->data.str.buff[jsp->data.str.buff_pos] = '\0';
|
||||||
|
|
||||||
|
/*
|
||||||
|
* - For array or key types - following one is always string
|
||||||
|
* - For object type - character is key
|
||||||
|
*/
|
||||||
|
SEND_EVT(jsp, (t == LWJSON_STREAM_TYPE_KEY || t == LWJSON_STREAM_TYPE_ARRAY)
|
||||||
|
? LWJSON_STREAM_TYPE_STRING
|
||||||
|
: LWJSON_STREAM_TYPE_KEY);
|
||||||
|
jsp->data.str.buff_pos = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse any type of primitive that is not a string.
|
||||||
|
*
|
||||||
|
* true, false, null or any number primitive
|
||||||
|
*/
|
||||||
|
case LWJSON_STREAM_STATE_PARSING_PRIMITIVE:
|
||||||
|
{
|
||||||
|
/* Any character except space, comma, or end of array/object are valid */
|
||||||
|
if (!prv_is_space_char_ext(c) && c != ',' && c != ']' && c != '}')
|
||||||
|
{
|
||||||
|
if (jsp->data.prim.buff_pos < sizeof(jsp->data.prim.buff) - 1)
|
||||||
|
{
|
||||||
|
jsp->data.prim.buff[jsp->data.prim.buff_pos++] = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lwjson_stream_type_t t = prv_stack_get_top(jsp);
|
||||||
|
|
||||||
|
#if defined(LWJSON_DEV)
|
||||||
|
if (t == LWJSON_STREAM_TYPE_OBJECT)
|
||||||
|
{
|
||||||
|
/* TODO: Handle error - primitive cannot be just after object */
|
||||||
|
}
|
||||||
|
else if (t == LWJSON_STREAM_TYPE_KEY)
|
||||||
|
{
|
||||||
|
LWJSON_DEBUG(
|
||||||
|
jsp,
|
||||||
|
"End of primitive parsing - string value associated to previous key in an object: \"%s\"\r\n",
|
||||||
|
jsp->data.prim.buff);
|
||||||
|
}
|
||||||
|
else if (t == LWJSON_STREAM_TYPE_ARRAY)
|
||||||
|
{
|
||||||
|
LWJSON_DEBUG(jsp, "End of primitive parsing - an array string entry: \"%s\"\r\n",
|
||||||
|
jsp->data.prim.buff);
|
||||||
|
}
|
||||||
|
#endif /* defined(LWJSON_DEV) */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is the end of primitive parsing
|
||||||
|
*
|
||||||
|
* It is assumed that buffer for primitive can handle at least
|
||||||
|
* true, false, null or all number characters (that being real or int number)
|
||||||
|
*/
|
||||||
|
if (jsp->data.prim.buff_pos == 4 && strncmp(jsp->data.prim.buff, "true", 4) == 0)
|
||||||
|
{
|
||||||
|
LWJSON_DEBUG(jsp, "Primitive parsed as %s\r\n", "true");
|
||||||
|
SEND_EVT(jsp, LWJSON_STREAM_TYPE_TRUE);
|
||||||
|
}
|
||||||
|
else if (jsp->data.prim.buff_pos == 4 && strncmp(jsp->data.prim.buff, "null", 4) == 0)
|
||||||
|
{
|
||||||
|
LWJSON_DEBUG(jsp, "Primitive parsed as %s\r\n", "null");
|
||||||
|
SEND_EVT(jsp, LWJSON_STREAM_TYPE_NULL);
|
||||||
|
}
|
||||||
|
else if (jsp->data.prim.buff_pos == 5 && strncmp(jsp->data.prim.buff, "false", 5) == 0)
|
||||||
|
{
|
||||||
|
LWJSON_DEBUG(jsp, "Primitive parsed as %s\r\n", "false");
|
||||||
|
SEND_EVT(jsp, LWJSON_STREAM_TYPE_FALSE);
|
||||||
|
}
|
||||||
|
else if (jsp->data.prim.buff[0] == '-' || (jsp->data.prim.buff[0] >= '0' && jsp->data.prim.buff[0] <= '9'))
|
||||||
|
{
|
||||||
|
LWJSON_DEBUG(jsp, "Primitive parsed - number\r\n");
|
||||||
|
SEND_EVT(jsp, LWJSON_STREAM_TYPE_NUMBER);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LWJSON_DEBUG(jsp, "Invalid primitive type. Got: %s\r\n", jsp->data.prim.buff);
|
||||||
|
}
|
||||||
|
if (t == LWJSON_STREAM_TYPE_KEY)
|
||||||
|
{
|
||||||
|
prv_stack_pop(jsp);
|
||||||
|
}
|
||||||
|
else if (t == LWJSON_STREAM_TYPE_ARRAY)
|
||||||
|
{
|
||||||
|
jsp->stack[jsp->stack_pos - 1].meta.index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Received character is not part of the primitive and must be processed again
|
||||||
|
*
|
||||||
|
* Set state to default state and start from beginning
|
||||||
|
*/
|
||||||
|
jsp->parse_state = LWJSON_STREAM_STATE_PARSING;
|
||||||
|
goto start_over;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: Add other case statements */
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
jsp->prev_c = c; /* Save current c as previous for next round */
|
||||||
|
return lwjsonSTREAMINPROG;
|
||||||
|
}
|
||||||
723
ESP/lib/src/network/OTA/Github_OTA/lib/SemVer/semver.c
Normal file
723
ESP/lib/src/network/OTA/Github_OTA/lib/SemVer/semver.c
Normal file
@ -0,0 +1,723 @@
|
|||||||
|
/*
|
||||||
|
* semver.c
|
||||||
|
*
|
||||||
|
* Copyright (c) 2015-2017 Tomas Aparicio
|
||||||
|
* MIT licensed
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "semver.h"
|
||||||
|
|
||||||
|
#define SLICE_SIZE 50
|
||||||
|
#define DELIMITER "."
|
||||||
|
#define PR_DELIMITER "-"
|
||||||
|
#define MT_DELIMITER "+"
|
||||||
|
#define NUMBERS "0123456789"
|
||||||
|
#define ALPHA "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
|
#define DELIMITERS DELIMITER PR_DELIMITER MT_DELIMITER
|
||||||
|
#define VALID_CHARS NUMBERS ALPHA DELIMITERS
|
||||||
|
|
||||||
|
static const size_t MAX_SIZE = sizeof(char) * 255;
|
||||||
|
static const int MAX_SAFE_INT = (unsigned int)-1 >> 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define comparison operators, storing the
|
||||||
|
* ASCII code per each symbol in hexadecimal notation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
enum operators
|
||||||
|
{
|
||||||
|
SYMBOL_GT = 0x3e,
|
||||||
|
SYMBOL_LT = 0x3c,
|
||||||
|
SYMBOL_EQ = 0x3d,
|
||||||
|
SYMBOL_TF = 0x7e,
|
||||||
|
SYMBOL_CF = 0x5e
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private helpers
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Remove [begin:len-begin] from str by moving len data from begin+len to begin.
|
||||||
|
* If len is negative cut out to the end of the string.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
strcut(char *str, int begin, int len)
|
||||||
|
{
|
||||||
|
size_t l;
|
||||||
|
l = strlen(str);
|
||||||
|
|
||||||
|
if ((int)l < 0 || (int)l > MAX_SAFE_INT)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (len < 0)
|
||||||
|
len = l - begin + 1;
|
||||||
|
if (begin + len > (int)l)
|
||||||
|
len = l - begin;
|
||||||
|
memmove(str + begin, str + begin + len, l - len + 1 - begin);
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
contains(const char c, const char *matrix, size_t len)
|
||||||
|
{
|
||||||
|
size_t x;
|
||||||
|
for (x = 0; x < len; x++)
|
||||||
|
if ((char)matrix[x] == c)
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
has_valid_chars(const char *str, const char *matrix)
|
||||||
|
{
|
||||||
|
size_t i, len, mlen;
|
||||||
|
len = strlen(str);
|
||||||
|
mlen = strlen(matrix);
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
|
if (contains(str[i], matrix, mlen) == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
binary_comparison(int x, int y)
|
||||||
|
{
|
||||||
|
if (x == y)
|
||||||
|
return 0;
|
||||||
|
if (x > y)
|
||||||
|
return 1;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
parse_int(const char *s)
|
||||||
|
{
|
||||||
|
int valid, num;
|
||||||
|
valid = has_valid_chars(s, NUMBERS);
|
||||||
|
if (valid == 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
num = strtol(s, NULL, 10);
|
||||||
|
if (num > MAX_SAFE_INT)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return a string allocated on the heap with the content from sep to end and
|
||||||
|
* terminate buf at sep.
|
||||||
|
*/
|
||||||
|
static char *
|
||||||
|
parse_slice(char *buf, char sep)
|
||||||
|
{
|
||||||
|
char *pr, *part;
|
||||||
|
int plen;
|
||||||
|
|
||||||
|
/* Find separator in buf */
|
||||||
|
pr = strchr(buf, sep);
|
||||||
|
if (pr == NULL)
|
||||||
|
return NULL;
|
||||||
|
/* Length from separator to end of buf */
|
||||||
|
plen = strlen(pr);
|
||||||
|
|
||||||
|
/* Copy from buf into new string */
|
||||||
|
part = (char *)calloc(plen + 1, sizeof(*part));
|
||||||
|
if (part == NULL)
|
||||||
|
return NULL;
|
||||||
|
memcpy(part, pr + 1, plen);
|
||||||
|
/* Null terminate new string */
|
||||||
|
part[plen] = '\0';
|
||||||
|
|
||||||
|
/* Terminate buf where separator was */
|
||||||
|
*pr = '\0';
|
||||||
|
|
||||||
|
return part;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a string as semver expression.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
*
|
||||||
|
* `0` - Parsed successfully
|
||||||
|
* `-1` - In case of error
|
||||||
|
*/
|
||||||
|
int semver_parse(const char *str, semver_t *ver)
|
||||||
|
{
|
||||||
|
int valid, res;
|
||||||
|
size_t len;
|
||||||
|
char *buf;
|
||||||
|
bzero(ver, sizeof(semver_t));
|
||||||
|
valid = semver_is_valid(str);
|
||||||
|
if (!valid)
|
||||||
|
return -1;
|
||||||
|
if (*str == 'v')
|
||||||
|
str++;
|
||||||
|
|
||||||
|
len = strlen(str);
|
||||||
|
buf = (char *)calloc(len + 1, sizeof(*buf));
|
||||||
|
if (buf == NULL)
|
||||||
|
return -1;
|
||||||
|
strcpy(buf, str);
|
||||||
|
|
||||||
|
ver->metadata = parse_slice(buf, MT_DELIMITER[0]);
|
||||||
|
ver->prerelease = parse_slice(buf, PR_DELIMITER[0]);
|
||||||
|
|
||||||
|
res = semver_parse_version(buf, ver);
|
||||||
|
#if DEBUG > 0
|
||||||
|
printf("[debug] semver.c %s (%s) = %d.%d.%d - %d\n", str, buf, ver->major, ver->minor, ver->patch, res);
|
||||||
|
if (ver->prerelease)
|
||||||
|
printf("[debug] semver.c prerelease: %s\n", ver->prerelease);
|
||||||
|
if (ver->metadata)
|
||||||
|
printf("[debug] semver.c metadata: %s\n", ver->metadata);
|
||||||
|
// printf("[debug] semver.c %s = %d.%d.%d, %s %s\n", str, ver->major, ver->minor, ver->patch, ver->prerelease, ver->metadata);
|
||||||
|
#endif
|
||||||
|
free(buf);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a given string as semver expression.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
*
|
||||||
|
* `0` - Parsed successfully
|
||||||
|
* `-1` - Parse error or invalid
|
||||||
|
*/
|
||||||
|
|
||||||
|
int semver_parse_version(const char *str, semver_t *ver)
|
||||||
|
{
|
||||||
|
size_t len;
|
||||||
|
int index, value;
|
||||||
|
char *slice, *next, *endptr;
|
||||||
|
slice = (char *)str;
|
||||||
|
index = 0;
|
||||||
|
|
||||||
|
while (slice != NULL && index++ < 4)
|
||||||
|
{
|
||||||
|
next = strchr(slice, DELIMITER[0]);
|
||||||
|
if (next == NULL)
|
||||||
|
len = strlen(slice);
|
||||||
|
else
|
||||||
|
len = next - slice;
|
||||||
|
if (len > SLICE_SIZE)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* Cast to integer and store */
|
||||||
|
value = strtol(slice, &endptr, 10);
|
||||||
|
if (endptr != next && *endptr != '\0')
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
switch (index)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
ver->major = value;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
ver->minor = value;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
ver->patch = value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Continue with the next slice */
|
||||||
|
if (next == NULL)
|
||||||
|
slice = NULL;
|
||||||
|
else
|
||||||
|
slice = next + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
compare_prerelease(char *x, char *y)
|
||||||
|
{
|
||||||
|
char *lastx, *lasty, *xptr, *yptr, *endptr;
|
||||||
|
int xlen, ylen, xisnum, yisnum, xnum, ynum;
|
||||||
|
int xn, yn, min, res;
|
||||||
|
if (x == NULL && y == NULL)
|
||||||
|
return 0;
|
||||||
|
if (y == NULL && x)
|
||||||
|
return -1;
|
||||||
|
if (x == NULL && y)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
lastx = x;
|
||||||
|
lasty = y;
|
||||||
|
xlen = strlen(x);
|
||||||
|
ylen = strlen(y);
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
if ((xptr = strchr(lastx, DELIMITER[0])) == NULL)
|
||||||
|
xptr = x + xlen;
|
||||||
|
if ((yptr = strchr(lasty, DELIMITER[0])) == NULL)
|
||||||
|
yptr = y + ylen;
|
||||||
|
|
||||||
|
xnum = strtol(lastx, &endptr, 10);
|
||||||
|
xisnum = endptr == xptr ? 1 : 0;
|
||||||
|
ynum = strtol(lasty, &endptr, 10);
|
||||||
|
yisnum = endptr == yptr ? 1 : 0;
|
||||||
|
|
||||||
|
if (xisnum && !yisnum)
|
||||||
|
return -1;
|
||||||
|
if (!xisnum && yisnum)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (xisnum && yisnum)
|
||||||
|
{
|
||||||
|
/* Numerical comparison */
|
||||||
|
if (xnum != ynum)
|
||||||
|
return xnum < ynum ? -1 : 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* String comparison */
|
||||||
|
xn = xptr - lastx;
|
||||||
|
yn = yptr - lasty;
|
||||||
|
min = xn < yn ? xn : yn;
|
||||||
|
if ((res = strncmp(lastx, lasty, min)))
|
||||||
|
return res < 0 ? -1 : 1;
|
||||||
|
if (xn != yn)
|
||||||
|
return xn < yn ? -1 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastx = xptr + 1;
|
||||||
|
lasty = yptr + 1;
|
||||||
|
if (lastx == x + xlen + 1 && lasty == y + ylen + 1)
|
||||||
|
break;
|
||||||
|
if (lastx == x + xlen + 1)
|
||||||
|
return -1;
|
||||||
|
if (lasty == y + ylen + 1)
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int semver_compare_prerelease(semver_t x, semver_t y)
|
||||||
|
{
|
||||||
|
return compare_prerelease(x.prerelease, y.prerelease);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a major, minor and patch binary comparison (x, y).
|
||||||
|
* This function is mostly used internally
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
*
|
||||||
|
* `0` - If versiona are equal
|
||||||
|
* `1` - If x is higher than y
|
||||||
|
* `-1` - If x is lower than y
|
||||||
|
*/
|
||||||
|
|
||||||
|
int semver_compare_version(semver_t x, semver_t y)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
|
||||||
|
if ((res = binary_comparison(x.major, y.major)) == 0)
|
||||||
|
{
|
||||||
|
if ((res = binary_comparison(x.minor, y.minor)) == 0)
|
||||||
|
{
|
||||||
|
return binary_comparison(x.patch, y.patch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compare two semantic versions (x, y).
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* - `1` if x is higher than y
|
||||||
|
* - `0` if x is equal to y
|
||||||
|
* - `-1` if x is lower than y
|
||||||
|
*/
|
||||||
|
|
||||||
|
int semver_compare(semver_t x, semver_t y)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
|
||||||
|
if ((res = semver_compare_version(x, y)) == 0)
|
||||||
|
{
|
||||||
|
return semver_compare_prerelease(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a `greater than` comparison
|
||||||
|
*/
|
||||||
|
|
||||||
|
int semver_gt(semver_t x, semver_t y)
|
||||||
|
{
|
||||||
|
return semver_compare(x, y) == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a `lower than` comparison
|
||||||
|
*/
|
||||||
|
|
||||||
|
int semver_lt(semver_t x, semver_t y)
|
||||||
|
{
|
||||||
|
return semver_compare(x, y) == -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a `equality` comparison
|
||||||
|
*/
|
||||||
|
|
||||||
|
int semver_eq(semver_t x, semver_t y)
|
||||||
|
{
|
||||||
|
return semver_compare(x, y) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a `non equal to` comparison
|
||||||
|
*/
|
||||||
|
|
||||||
|
int semver_neq(semver_t x, semver_t y)
|
||||||
|
{
|
||||||
|
return semver_compare(x, y) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a `greater than or equal` comparison
|
||||||
|
*/
|
||||||
|
|
||||||
|
int semver_gte(semver_t x, semver_t y)
|
||||||
|
{
|
||||||
|
return semver_compare(x, y) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a `lower than or equal` comparison
|
||||||
|
*/
|
||||||
|
|
||||||
|
int semver_lte(semver_t x, semver_t y)
|
||||||
|
{
|
||||||
|
return semver_compare(x, y) <= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if version `x` can be satisfied by `y`
|
||||||
|
* performing a comparison with caret operator.
|
||||||
|
*
|
||||||
|
* See: https://docs.npmjs.com/misc/semver#caret-ranges-1-2-3-0-2-5-0-0-4
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
*
|
||||||
|
* `1` - Can be satisfied
|
||||||
|
* `0` - Cannot be satisfied
|
||||||
|
*/
|
||||||
|
|
||||||
|
int semver_satisfies_caret(semver_t x, semver_t y)
|
||||||
|
{
|
||||||
|
/* Major versions must always match. */
|
||||||
|
if (x.major == y.major)
|
||||||
|
{
|
||||||
|
/* If major version is 0, minor versions must match */
|
||||||
|
if (x.major == 0)
|
||||||
|
{
|
||||||
|
/* If minor version is 0, patch must match */
|
||||||
|
if (x.minor == 0)
|
||||||
|
{
|
||||||
|
return (x.minor == y.minor) && (x.patch == y.patch);
|
||||||
|
}
|
||||||
|
/* If minor version is not 0, patch must be >= */
|
||||||
|
else if (x.minor == y.minor)
|
||||||
|
{
|
||||||
|
return x.patch >= y.patch;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (x.minor > y.minor)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else if (x.minor == y.minor)
|
||||||
|
{
|
||||||
|
return x.patch >= y.patch;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if version `x` can be satisfied by `y`
|
||||||
|
* performing a comparison with tilde operator.
|
||||||
|
*
|
||||||
|
* See: https://docs.npmjs.com/misc/semver#tilde-ranges-1-2-3-1-2-1
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
*
|
||||||
|
* `1` - Can be satisfied
|
||||||
|
* `0` - Cannot be satisfied
|
||||||
|
*/
|
||||||
|
|
||||||
|
int semver_satisfies_patch(semver_t x, semver_t y)
|
||||||
|
{
|
||||||
|
return x.major == y.major && x.minor == y.minor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if both versions can be satisfied
|
||||||
|
* based on the given comparison operator.
|
||||||
|
*
|
||||||
|
* Allowed operators:
|
||||||
|
*
|
||||||
|
* - `=` - Equality
|
||||||
|
* - `>=` - Higher or equal to
|
||||||
|
* - `<=` - Lower or equal to
|
||||||
|
* - `<` - Lower than
|
||||||
|
* - `>` - Higher than
|
||||||
|
* - `^` - Caret comparison (see https://docs.npmjs.com/misc/semver#caret-ranges-1-2-3-0-2-5-0-0-4)
|
||||||
|
* - `~` - Tilde comparison (see https://docs.npmjs.com/misc/semver#tilde-ranges-1-2-3-1-2-1)
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
*
|
||||||
|
* `1` - Can be satisfied
|
||||||
|
* `0` - Cannot be satisfied
|
||||||
|
*/
|
||||||
|
|
||||||
|
int semver_satisfies(semver_t x, semver_t y, const char *op)
|
||||||
|
{
|
||||||
|
int first, second;
|
||||||
|
/* Extract the comparison operator */
|
||||||
|
first = op[0];
|
||||||
|
second = op[1];
|
||||||
|
|
||||||
|
/* Caret operator */
|
||||||
|
if (first == SYMBOL_CF)
|
||||||
|
return semver_satisfies_caret(x, y);
|
||||||
|
|
||||||
|
/* Tilde operator */
|
||||||
|
if (first == SYMBOL_TF)
|
||||||
|
return semver_satisfies_patch(x, y);
|
||||||
|
|
||||||
|
/* Strict equality */
|
||||||
|
if (first == SYMBOL_EQ)
|
||||||
|
return semver_eq(x, y);
|
||||||
|
|
||||||
|
/* Greater than or equal comparison */
|
||||||
|
if (first == SYMBOL_GT)
|
||||||
|
{
|
||||||
|
if (second == SYMBOL_EQ)
|
||||||
|
{
|
||||||
|
return semver_gte(x, y);
|
||||||
|
}
|
||||||
|
return semver_gt(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Lower than or equal comparison */
|
||||||
|
if (first == SYMBOL_LT)
|
||||||
|
{
|
||||||
|
if (second == SYMBOL_EQ)
|
||||||
|
{
|
||||||
|
return semver_lte(x, y);
|
||||||
|
}
|
||||||
|
return semver_lt(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free heep allocated memory of a given semver.
|
||||||
|
* This is just a convenient function that you
|
||||||
|
* should call when you're done.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void semver_free(semver_t *x)
|
||||||
|
{
|
||||||
|
if (x->metadata)
|
||||||
|
{
|
||||||
|
free(x->metadata);
|
||||||
|
x->metadata = NULL;
|
||||||
|
}
|
||||||
|
if (x->prerelease)
|
||||||
|
{
|
||||||
|
free(x->prerelease);
|
||||||
|
x->prerelease = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void
|
||||||
|
concat_num(char *str, int x, const char *sep)
|
||||||
|
{
|
||||||
|
char buf[SLICE_SIZE] = {0};
|
||||||
|
if (sep == NULL)
|
||||||
|
sprintf(buf, "%d", x);
|
||||||
|
else
|
||||||
|
sprintf(buf, "%s%d", sep, x);
|
||||||
|
strcat(str, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
concat_char(char *str, char *x, const char *sep)
|
||||||
|
{
|
||||||
|
char buf[SLICE_SIZE] = {0};
|
||||||
|
sprintf(buf, "%s%s", sep, x);
|
||||||
|
strcat(str, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render a given semver as string
|
||||||
|
*/
|
||||||
|
|
||||||
|
void semver_render(semver_t *x, char *dest)
|
||||||
|
{
|
||||||
|
concat_num(dest, x->major, NULL);
|
||||||
|
concat_num(dest, x->minor, DELIMITER);
|
||||||
|
concat_num(dest, x->patch, DELIMITER);
|
||||||
|
if (x->prerelease)
|
||||||
|
concat_char(dest, x->prerelease, PR_DELIMITER);
|
||||||
|
if (x->metadata)
|
||||||
|
concat_char(dest, x->metadata, MT_DELIMITER);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Version bump helpers
|
||||||
|
*/
|
||||||
|
|
||||||
|
void semver_bump(semver_t *x)
|
||||||
|
{
|
||||||
|
x->major++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void semver_bump_minor(semver_t *x)
|
||||||
|
{
|
||||||
|
x->minor++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void semver_bump_patch(semver_t *x)
|
||||||
|
{
|
||||||
|
x->patch++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helpers
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int
|
||||||
|
has_valid_length(const char *s)
|
||||||
|
{
|
||||||
|
return strlen(s) <= MAX_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a given semver string is valid
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
*
|
||||||
|
* `1` - Valid expression
|
||||||
|
* `0` - Invalid
|
||||||
|
*/
|
||||||
|
|
||||||
|
int semver_is_valid(const char *s)
|
||||||
|
{
|
||||||
|
return has_valid_length(s) && has_valid_chars(s, VALID_CHARS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes non-valid characters in the given string.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
*
|
||||||
|
* `0` - Valid
|
||||||
|
* `-1` - Invalid input
|
||||||
|
*/
|
||||||
|
|
||||||
|
int semver_clean(char *s)
|
||||||
|
{
|
||||||
|
size_t i, len, mlen;
|
||||||
|
int res;
|
||||||
|
if (has_valid_length(s) == 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
len = strlen(s);
|
||||||
|
mlen = strlen(VALID_CHARS);
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
|
{
|
||||||
|
if (contains(s[i], VALID_CHARS, mlen) == 0)
|
||||||
|
{
|
||||||
|
res = strcut(s, i, 1);
|
||||||
|
if (res == -1)
|
||||||
|
return -1;
|
||||||
|
--len;
|
||||||
|
--i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
char_to_int(const char *str)
|
||||||
|
{
|
||||||
|
int buf;
|
||||||
|
size_t i, len, mlen;
|
||||||
|
buf = 0;
|
||||||
|
len = strlen(str);
|
||||||
|
mlen = strlen(VALID_CHARS);
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
|
if (contains(str[i], VALID_CHARS, mlen))
|
||||||
|
buf += (int)str[i];
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render a given semver as numeric value.
|
||||||
|
* Useful for ordering and filtering.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int semver_numeric(semver_t *x)
|
||||||
|
{
|
||||||
|
int num;
|
||||||
|
char buf[SLICE_SIZE * 3];
|
||||||
|
memset(&buf, 0, SLICE_SIZE * 3);
|
||||||
|
|
||||||
|
if (x->major)
|
||||||
|
concat_num(buf, x->major, NULL);
|
||||||
|
if (x->major || x->minor)
|
||||||
|
concat_num(buf, x->minor, NULL);
|
||||||
|
if (x->major || x->minor || x->patch)
|
||||||
|
concat_num(buf, x->patch, NULL);
|
||||||
|
|
||||||
|
num = parse_int(buf);
|
||||||
|
if (num == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (x->prerelease)
|
||||||
|
num += char_to_int(x->prerelease);
|
||||||
|
if (x->metadata)
|
||||||
|
num += char_to_int(x->metadata);
|
||||||
|
|
||||||
|
return num;
|
||||||
|
}
|
||||||
107
ESP/lib/src/network/OTA/Github_OTA/lib/SemVer/semver.h
Normal file
107
ESP/lib/src/network/OTA/Github_OTA/lib/SemVer/semver.h
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
/*
|
||||||
|
* semver.h
|
||||||
|
*
|
||||||
|
* Copyright (c) 2015-2017 Tomas Aparicio
|
||||||
|
* MIT licensed
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __SEMVER_H
|
||||||
|
#define __SEMVER_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SEMVER_VERSION
|
||||||
|
#define SEMVER_VERSION "0.2.0"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* semver_t struct
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct semver_version_s
|
||||||
|
{
|
||||||
|
int major;
|
||||||
|
int minor;
|
||||||
|
int patch;
|
||||||
|
char *metadata;
|
||||||
|
char *prerelease;
|
||||||
|
} semver_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set prototypes
|
||||||
|
*/
|
||||||
|
|
||||||
|
int
|
||||||
|
semver_satisfies(semver_t x, semver_t y, const char *op);
|
||||||
|
|
||||||
|
int
|
||||||
|
semver_satisfies_caret(semver_t x, semver_t y);
|
||||||
|
|
||||||
|
int
|
||||||
|
semver_satisfies_patch(semver_t x, semver_t y);
|
||||||
|
|
||||||
|
int
|
||||||
|
semver_compare(semver_t x, semver_t y);
|
||||||
|
|
||||||
|
int
|
||||||
|
semver_compare_version(semver_t x, semver_t y);
|
||||||
|
|
||||||
|
int
|
||||||
|
semver_compare_prerelease(semver_t x, semver_t y);
|
||||||
|
|
||||||
|
int
|
||||||
|
semver_gt(semver_t x, semver_t y);
|
||||||
|
|
||||||
|
int
|
||||||
|
semver_gte(semver_t x, semver_t y);
|
||||||
|
|
||||||
|
int
|
||||||
|
semver_lt(semver_t x, semver_t y);
|
||||||
|
|
||||||
|
int
|
||||||
|
semver_lte(semver_t x, semver_t y);
|
||||||
|
|
||||||
|
int
|
||||||
|
semver_eq(semver_t x, semver_t y);
|
||||||
|
|
||||||
|
int
|
||||||
|
semver_neq(semver_t x, semver_t y);
|
||||||
|
|
||||||
|
int
|
||||||
|
semver_parse(const char *str, semver_t *ver);
|
||||||
|
|
||||||
|
int
|
||||||
|
semver_parse_version(const char *str, semver_t *ver);
|
||||||
|
|
||||||
|
void
|
||||||
|
semver_render(semver_t *x, char *dest);
|
||||||
|
|
||||||
|
int
|
||||||
|
semver_numeric(semver_t *x);
|
||||||
|
|
||||||
|
void
|
||||||
|
semver_bump(semver_t *x);
|
||||||
|
|
||||||
|
void
|
||||||
|
semver_bump_minor(semver_t *x);
|
||||||
|
|
||||||
|
void
|
||||||
|
semver_bump_patch(semver_t *x);
|
||||||
|
|
||||||
|
void
|
||||||
|
semver_free(semver_t *x);
|
||||||
|
|
||||||
|
int
|
||||||
|
semver_is_valid(const char *s);
|
||||||
|
|
||||||
|
int
|
||||||
|
semver_clean(char *s);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
861
ESP/lib/src/network/OTA/Github_OTA/lib/esp_ota.c
Normal file
861
ESP/lib/src/network/OTA/Github_OTA/lib/esp_ota.c
Normal file
@ -0,0 +1,861 @@
|
|||||||
|
#include <stdlib.h>
|
||||||
|
#include <fnmatch.h>
|
||||||
|
#include <libgen.h>
|
||||||
|
#include <freertos/FreeRTOS.h>
|
||||||
|
#include <freertos/task.h>
|
||||||
|
#include <freertos/event_groups.h>
|
||||||
|
#include <esp_http_client.h>
|
||||||
|
#include <esp_tls.h>
|
||||||
|
#include <esp_crt_bundle.h>
|
||||||
|
#include <esp_log.h>
|
||||||
|
#include <esp_app_format.h>
|
||||||
|
#include <esp_ota_ops.h>
|
||||||
|
#include <esp_https_ota.h>
|
||||||
|
#include <esp_event.h>
|
||||||
|
|
||||||
|
#include <sdkconfig.h>
|
||||||
|
#include "esp_ota.h"
|
||||||
|
#include "LWJSON/lwjson.h"
|
||||||
|
|
||||||
|
ESP_EVENT_DEFINE_BASE(Github_OTA_EVENTS);
|
||||||
|
|
||||||
|
typedef struct Github_OTA_client_handle_t
|
||||||
|
{
|
||||||
|
Github_OTA_config_t config;
|
||||||
|
char *username;
|
||||||
|
char *token;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
char tag_name[CONFIG_MAX_FILENAME_LEN];
|
||||||
|
char name[CONFIG_MAX_FILENAME_LEN];
|
||||||
|
char url[CONFIG_MAX_URL_LEN];
|
||||||
|
char storageurl[CONFIG_MAX_URL_LEN];
|
||||||
|
uint8_t flags;
|
||||||
|
} result;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
char name[CONFIG_MAX_FILENAME_LEN];
|
||||||
|
char url[CONFIG_MAX_URL_LEN];
|
||||||
|
} scratch;
|
||||||
|
semver_t current_version;
|
||||||
|
semver_t latest_version;
|
||||||
|
uint32_t countdown;
|
||||||
|
TaskHandle_t task_handle;
|
||||||
|
const esp_partition_t *storage_partition;
|
||||||
|
} Github_OTA_client_handle_t;
|
||||||
|
|
||||||
|
enum release_flags
|
||||||
|
{
|
||||||
|
Github_OTA_RELEASE_GOT_TAG = 0x01,
|
||||||
|
Github_OTA_RELEASE_GOT_FNAME = 0x02,
|
||||||
|
Github_OTA_RELEASE_GOT_URL = 0x04,
|
||||||
|
Github_OTA_RELEASE_GOT_STORAGE = 0x08,
|
||||||
|
Github_OTA_RELEASE_VALID_ASSET = 0x10,
|
||||||
|
} release_flags;
|
||||||
|
|
||||||
|
SemaphoreHandle_t Github_OTA_lock = NULL;
|
||||||
|
|
||||||
|
static void SetFlag(Github_OTA_client_handle_t *handle, enum release_flags flag)
|
||||||
|
{
|
||||||
|
handle->result.flags |= flag;
|
||||||
|
}
|
||||||
|
static bool GetFlag(Github_OTA_client_handle_t *handle, enum release_flags flag)
|
||||||
|
{
|
||||||
|
return handle->result.flags & flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ClearFlag(Github_OTA_client_handle_t *handle, enum release_flags flag)
|
||||||
|
{
|
||||||
|
handle->result.flags &= ~flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
Github_OTA_client_handle_t *Github_OTA_init(Github_OTA_config_t *newconfig)
|
||||||
|
{
|
||||||
|
if (!Github_OTA_lock)
|
||||||
|
{
|
||||||
|
Github_OTA_lock = xSemaphoreCreateMutex();
|
||||||
|
}
|
||||||
|
if (xSemaphoreTake(Github_OTA_lock, pdMS_TO_TICKS(1000)) != pdPASS)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "Failed to take lock");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
Github_OTA_client_handle_t *handle = malloc(sizeof(Github_OTA_client_handle_t));
|
||||||
|
if (handle == NULL)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "Failed to allocate memory for client handle");
|
||||||
|
xSemaphoreGive(Github_OTA_lock);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
bzero(handle, sizeof(Github_OTA_client_handle_t));
|
||||||
|
strncpy(handle->config.filenamematch, newconfig->filenamematch, CONFIG_MAX_FILENAME_LEN);
|
||||||
|
strncpy(handle->config.storagenamematch, newconfig->storagenamematch, CONFIG_MAX_FILENAME_LEN);
|
||||||
|
strncpy(handle->config.storagepartitionname, newconfig->storagepartitionname, 17);
|
||||||
|
if (newconfig->hostname == NULL)
|
||||||
|
asprintf(&handle->config.hostname, CONFIG_GITHUB_HOSTNAME);
|
||||||
|
else
|
||||||
|
asprintf(&handle->config.hostname, newconfig->hostname);
|
||||||
|
|
||||||
|
if (newconfig->orgname == NULL)
|
||||||
|
asprintf(&handle->config.orgname, CONFIG_GITHUB_OWNER);
|
||||||
|
else
|
||||||
|
asprintf(&handle->config.orgname, newconfig->orgname);
|
||||||
|
|
||||||
|
if (newconfig->reponame == NULL)
|
||||||
|
asprintf(&handle->config.reponame, CONFIG_GITHUB_REPO);
|
||||||
|
else
|
||||||
|
asprintf(&handle->config.reponame, newconfig->reponame);
|
||||||
|
|
||||||
|
const esp_app_desc_t *app_desc = esp_ota_get_app_description();
|
||||||
|
if (semver_parse(app_desc->version, &handle->current_version))
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "Failed to parse current version");
|
||||||
|
Github_OTA_free(handle);
|
||||||
|
xSemaphoreGive(Github_OTA_lock);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
handle->result.flags = 0;
|
||||||
|
|
||||||
|
// if (newconfig->updateInterval < 60) {
|
||||||
|
// ESP_LOGE(TAG, "Update interval must be at least 60 Minutes");
|
||||||
|
// newconfig->updateInterval = 60;
|
||||||
|
// }
|
||||||
|
handle->config.updateInterval = newconfig->updateInterval;
|
||||||
|
|
||||||
|
handle->task_handle = NULL;
|
||||||
|
xSemaphoreGive(Github_OTA_lock);
|
||||||
|
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t Github_OTA_free(Github_OTA_client_handle_t *handle)
|
||||||
|
{
|
||||||
|
if (xSemaphoreTake(Github_OTA_lock, pdMS_TO_TICKS(1000)) != pdPASS)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "Failed to take lock");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
free(handle->config.hostname);
|
||||||
|
free(handle->config.orgname);
|
||||||
|
free(handle->config.reponame);
|
||||||
|
if (handle->username)
|
||||||
|
free(handle->username);
|
||||||
|
if (handle->token)
|
||||||
|
free(handle->token);
|
||||||
|
semver_free(&handle->current_version);
|
||||||
|
semver_free(&handle->latest_version);
|
||||||
|
xSemaphoreGive(Github_OTA_lock);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t Github_OTA_set_auth(Github_OTA_client_handle_t *handle, const char *username, const char *password)
|
||||||
|
{
|
||||||
|
if (xSemaphoreTake(Github_OTA_lock, pdMS_TO_TICKS(1000)) != pdPASS)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "Failed to take lock");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
asprintf(&handle->username, "%s", username);
|
||||||
|
asprintf(&handle->token, "%s", password);
|
||||||
|
xSemaphoreGive(Github_OTA_lock);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lwjson_callback(lwjson_stream_parser_t *jsp, lwjson_stream_type_t type)
|
||||||
|
{
|
||||||
|
if (jsp->udata == NULL)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "No user data for callback");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Github_OTA_client_handle_t *handle = (Github_OTA_client_handle_t *)jsp->udata;
|
||||||
|
#ifdef DEBUG
|
||||||
|
ESP_LOGI(TAG, "Lwjson Called: %d %d %d %d", jsp->stack_pos, jsp->stack[jsp->stack_pos - 1].type, type, handle->result.flags);
|
||||||
|
if (jsp->stack[jsp->stack_pos - 1].type == LWJSON_STREAM_TYPE_KEY)
|
||||||
|
{ /* We need key to be before */
|
||||||
|
ESP_LOGI(TAG, "Key: %s", jsp->stack[jsp->stack_pos - 1].meta.name);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
/* Get a value corresponsing to "tag_name" key */
|
||||||
|
if (!GetFlag(handle, Github_OTA_RELEASE_GOT_TAG))
|
||||||
|
{
|
||||||
|
if (jsp->stack_pos >= 2 /* Number of stack entries must be high */
|
||||||
|
&& jsp->stack[0].type == LWJSON_STREAM_TYPE_OBJECT /* First must be object */
|
||||||
|
&& jsp->stack[1].type == LWJSON_STREAM_TYPE_KEY /* We need key to be before */
|
||||||
|
&& strcasecmp(jsp->stack[1].meta.name, "tag_name") == 0)
|
||||||
|
{
|
||||||
|
ESP_LOGD(TAG, "Got '%s' with value '%s'", jsp->stack[1].meta.name, jsp->data.str.buff);
|
||||||
|
strncpy(handle->result.tag_name, jsp->data.str.buff, CONFIG_MAX_FILENAME_LEN);
|
||||||
|
SetFlag(handle, Github_OTA_RELEASE_GOT_TAG);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!GetFlag(handle, Github_OTA_RELEASE_VALID_ASSET) || !GetFlag(handle, Github_OTA_RELEASE_GOT_STORAGE))
|
||||||
|
{
|
||||||
|
if (jsp->stack_pos == 5 && jsp->stack[0].type == LWJSON_STREAM_TYPE_OBJECT && jsp->stack[1].type == LWJSON_STREAM_TYPE_KEY && strcasecmp(jsp->stack[1].meta.name, "assets") == 0 && jsp->stack[2].type == LWJSON_STREAM_TYPE_ARRAY && jsp->stack[3].type == LWJSON_STREAM_TYPE_OBJECT && jsp->stack[4].type == LWJSON_STREAM_TYPE_KEY)
|
||||||
|
{
|
||||||
|
ESP_LOGD(TAG, "Assets Got key '%s' with value '%s'", jsp->stack[jsp->stack_pos - 1].meta.name, jsp->data.str.buff);
|
||||||
|
if (strcasecmp(jsp->stack[4].meta.name, "name") == 0)
|
||||||
|
{
|
||||||
|
strncpy(handle->scratch.name, jsp->data.str.buff, CONFIG_MAX_FILENAME_LEN);
|
||||||
|
SetFlag(handle, Github_OTA_RELEASE_GOT_FNAME);
|
||||||
|
ESP_LOGD(TAG, "Got Filename for Asset: %s", handle->scratch.name);
|
||||||
|
}
|
||||||
|
if (strcasecmp(jsp->stack[4].meta.name, "url") == 0)
|
||||||
|
{
|
||||||
|
strncpy(handle->scratch.url, jsp->data.str.buff, CONFIG_MAX_URL_LEN);
|
||||||
|
SetFlag(handle, Github_OTA_RELEASE_GOT_URL);
|
||||||
|
ESP_LOGD(TAG, "Got URL for Asset: %s", handle->scratch.url);
|
||||||
|
}
|
||||||
|
/* Now test if we got both name an download url */
|
||||||
|
if (GetFlag(handle, Github_OTA_RELEASE_GOT_FNAME) && GetFlag(handle, Github_OTA_RELEASE_GOT_URL))
|
||||||
|
{
|
||||||
|
ESP_LOGD(TAG, "Testing Firmware filenames %s -> %s - Matching Filename against %s and %s", handle->scratch.name, handle->scratch.url, handle->config.filenamematch, handle->config.storagenamematch);
|
||||||
|
/* see if the filename matches */
|
||||||
|
if (!GetFlag(handle, Github_OTA_RELEASE_VALID_ASSET) && fnmatch(handle->config.filenamematch, handle->scratch.name, 0) == 0)
|
||||||
|
{
|
||||||
|
strncpy(handle->result.name, handle->scratch.name, CONFIG_MAX_FILENAME_LEN);
|
||||||
|
strncpy(handle->result.url, handle->scratch.url, CONFIG_MAX_URL_LEN);
|
||||||
|
ESP_LOGD(TAG, "Valid Firmware Found: %s - %s", handle->result.name, handle->result.url);
|
||||||
|
SetFlag(handle, Github_OTA_RELEASE_VALID_ASSET);
|
||||||
|
}
|
||||||
|
else if (!GetFlag(handle, Github_OTA_RELEASE_GOT_STORAGE) && fnmatch(handle->config.storagenamematch, handle->scratch.name, 0) == 0)
|
||||||
|
{
|
||||||
|
strncpy(handle->result.storageurl, handle->scratch.url, CONFIG_MAX_URL_LEN);
|
||||||
|
ESP_LOGD(TAG, "Valid Storage Asset Found: %s - %s", handle->scratch.name, handle->result.storageurl);
|
||||||
|
SetFlag(handle, Github_OTA_RELEASE_GOT_STORAGE);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ESP_LOGD(TAG, "Invalid Asset Found: %s", handle->scratch.name);
|
||||||
|
ClearFlag(handle, Github_OTA_RELEASE_GOT_FNAME);
|
||||||
|
ClearFlag(handle, Github_OTA_RELEASE_GOT_URL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t _http_event_handler(esp_http_client_event_t *evt)
|
||||||
|
{
|
||||||
|
lwjsonr_t res;
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wswitch"
|
||||||
|
switch (evt->event_id)
|
||||||
|
{
|
||||||
|
case HTTP_EVENT_ON_HEADER:
|
||||||
|
if (strncasecmp(evt->header_key, "x-ratelimit-remaining", strlen("x-ratelimit-remaining")) == 0)
|
||||||
|
{
|
||||||
|
int limit = atoi(evt->header_value);
|
||||||
|
ESP_LOGD(TAG, "Github API Rate Limit Remaining: %d", limit);
|
||||||
|
if (limit < 10)
|
||||||
|
{
|
||||||
|
ESP_LOGW(TAG, "Github API Rate Limit Remaining is low: %d", limit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case HTTP_EVENT_ON_DATA:
|
||||||
|
if (!esp_http_client_is_chunked_response(evt->client))
|
||||||
|
{
|
||||||
|
char *buf = evt->data;
|
||||||
|
for (int i = 0; i < evt->data_len; i++)
|
||||||
|
{
|
||||||
|
res = lwjson_stream_parse((lwjson_stream_parser_t *)evt->user_data, *buf);
|
||||||
|
if (!(res == lwjsonOK || res == lwjsonSTREAMDONE || res == lwjsonSTREAMINPROG))
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "Lwjson Error: %d", res);
|
||||||
|
}
|
||||||
|
buf++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case HTTP_EVENT_DISCONNECTED:
|
||||||
|
{
|
||||||
|
int mbedtls_err = 0;
|
||||||
|
esp_err_t err = esp_tls_get_and_clear_last_error(evt->data, &mbedtls_err, NULL);
|
||||||
|
if (err != 0)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "Last esp error code: 0x%x", err);
|
||||||
|
ESP_LOGE(TAG, "Last mbedtls failure: 0x%x", mbedtls_err);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t Github_OTA_check(Github_OTA_client_handle_t *handle)
|
||||||
|
{
|
||||||
|
if (xSemaphoreTake(Github_OTA_lock, pdMS_TO_TICKS(1000)) != pdPASS)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "Failed to get lock");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
ESP_LOGI(TAG, "Checking for new release");
|
||||||
|
ESP_ERROR_CHECK(esp_event_post(Github_OTA_EVENTS, Github_OTA_EVENT_START_CHECK, handle, sizeof(Github_OTA_client_handle_t *), portMAX_DELAY));
|
||||||
|
lwjson_stream_parser_t stream_parser;
|
||||||
|
lwjsonr_t res;
|
||||||
|
|
||||||
|
res = lwjson_stream_init(&stream_parser, lwjson_callback);
|
||||||
|
if (res != lwjsonOK)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "Failed to initialize JSON parser: %d", res);
|
||||||
|
ESP_ERROR_CHECK(esp_event_post(Github_OTA_EVENTS, Github_OTA_EVENT_NOUPDATE_AVAILABLE, handle, sizeof(Github_OTA_client_handle_t *), portMAX_DELAY));
|
||||||
|
xSemaphoreGive(Github_OTA_lock);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
stream_parser.udata = (void *)handle;
|
||||||
|
|
||||||
|
char url[CONFIG_MAX_URL_LEN];
|
||||||
|
snprintf(url, CONFIG_MAX_URL_LEN, "https://%s/repos/%s/%s/releases/latest", handle->config.hostname, handle->config.orgname, handle->config.reponame);
|
||||||
|
|
||||||
|
esp_http_client_config_t httpconfig = {
|
||||||
|
.url = url,
|
||||||
|
.crt_bundle_attach = esp_crt_bundle_attach,
|
||||||
|
.event_handler = _http_event_handler,
|
||||||
|
.user_data = &stream_parser,
|
||||||
|
};
|
||||||
|
if (handle->username)
|
||||||
|
{
|
||||||
|
ESP_LOGD(TAG, "Using Authenticated Request to %s", url);
|
||||||
|
httpconfig.username = handle->username;
|
||||||
|
httpconfig.password = handle->token;
|
||||||
|
httpconfig.auth_type = HTTP_AUTH_TYPE_BASIC;
|
||||||
|
}
|
||||||
|
ESP_LOGI(TAG, "Searching for Firmware from %s", url);
|
||||||
|
|
||||||
|
esp_http_client_handle_t client = esp_http_client_init(&httpconfig);
|
||||||
|
|
||||||
|
esp_err_t err = esp_http_client_perform(client);
|
||||||
|
if (err == ESP_OK)
|
||||||
|
{
|
||||||
|
ESP_LOGD(TAG, "HTTP GET Status = %d, content_length = %d",
|
||||||
|
esp_http_client_get_status_code(client),
|
||||||
|
esp_http_client_get_content_length(client));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "HTTP GET request failed: %s", esp_err_to_name(err));
|
||||||
|
esp_http_client_cleanup(client);
|
||||||
|
ESP_ERROR_CHECK(esp_event_post(Github_OTA_EVENTS, Github_OTA_EVENT_NOUPDATE_AVAILABLE, handle, sizeof(Github_OTA_client_handle_t *), portMAX_DELAY));
|
||||||
|
xSemaphoreGive(Github_OTA_lock);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
if (esp_http_client_get_status_code(client) == 200)
|
||||||
|
{
|
||||||
|
if (GetFlag(handle, Github_OTA_RELEASE_VALID_ASSET))
|
||||||
|
{
|
||||||
|
if (semver_parse(handle->result.tag_name, &handle->latest_version))
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "Failed to parse new version");
|
||||||
|
esp_http_client_cleanup(client);
|
||||||
|
ESP_ERROR_CHECK(esp_event_post(Github_OTA_EVENTS, Github_OTA_EVENT_NOUPDATE_AVAILABLE, handle, sizeof(Github_OTA_client_handle_t *), portMAX_DELAY));
|
||||||
|
xSemaphoreGive(Github_OTA_lock);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
ESP_LOGI(TAG, "Current Version %d.%d.%d", handle->current_version.major, handle->current_version.minor, handle->current_version.patch);
|
||||||
|
ESP_LOGI(TAG, "New Version %d.%d.%d", handle->latest_version.major, handle->latest_version.minor, handle->latest_version.patch);
|
||||||
|
ESP_LOGI(TAG, "Asset: %s", handle->result.name);
|
||||||
|
ESP_LOGI(TAG, "Firmware URL: %s", handle->result.url);
|
||||||
|
if (strlen(handle->result.storageurl))
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG, "Storage URL: %s", handle->result.storageurl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG, "Asset: No Valid Firmware Assets Found");
|
||||||
|
esp_http_client_cleanup(client);
|
||||||
|
ESP_ERROR_CHECK(esp_event_post(Github_OTA_EVENTS, Github_OTA_EVENT_NOUPDATE_AVAILABLE, handle, sizeof(Github_OTA_client_handle_t *), portMAX_DELAY));
|
||||||
|
xSemaphoreGive(Github_OTA_lock);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ESP_LOGW(TAG, "Github Release API Returned: %d", esp_http_client_get_status_code(client));
|
||||||
|
esp_http_client_cleanup(client);
|
||||||
|
ESP_ERROR_CHECK(esp_event_post(Github_OTA_EVENTS, Github_OTA_EVENT_NOUPDATE_AVAILABLE, handle, sizeof(Github_OTA_client_handle_t *), portMAX_DELAY));
|
||||||
|
xSemaphoreGive(Github_OTA_lock);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_http_client_cleanup(client);
|
||||||
|
ESP_ERROR_CHECK(esp_event_post(Github_OTA_EVENTS, Github_OTA_EVENT_UPDATE_AVAILABLE, handle, sizeof(Github_OTA_client_handle_t *), portMAX_DELAY));
|
||||||
|
xSemaphoreGive(Github_OTA_lock);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t validate_image_header(esp_app_desc_t *new_app_info)
|
||||||
|
{
|
||||||
|
if (new_app_info == NULL)
|
||||||
|
{
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
ESP_LOGI(TAG, "New Firmware Details:");
|
||||||
|
ESP_LOGI(TAG, "Project name: %s", new_app_info->project_name);
|
||||||
|
ESP_LOGI(TAG, "Firmware version: %s", new_app_info->version);
|
||||||
|
ESP_LOGI(TAG, "Compiled time: %s %s", new_app_info->date, new_app_info->time);
|
||||||
|
ESP_LOGI(TAG, "ESP-IDF: %s", new_app_info->idf_ver);
|
||||||
|
ESP_LOGI(TAG, "SHA256:");
|
||||||
|
ESP_LOG_BUFFER_HEX(TAG, new_app_info->app_elf_sha256, sizeof(new_app_info->app_elf_sha256));
|
||||||
|
|
||||||
|
const esp_partition_t *running = esp_ota_get_running_partition();
|
||||||
|
ESP_LOGD(TAG, "Current partition %s type %d subtype %d (offset 0x%08x)",
|
||||||
|
running->label, running->type, running->subtype, running->address);
|
||||||
|
const esp_partition_t *update = esp_ota_get_next_update_partition(NULL);
|
||||||
|
ESP_LOGD(TAG, "Update partition %s type %d subtype %d (offset 0x%08x)",
|
||||||
|
update->label, update->type, update->subtype, update->address);
|
||||||
|
|
||||||
|
#ifdef CONFIG_BOOTLOADER_APP_ANTI_ROLLBACK
|
||||||
|
/**
|
||||||
|
* Secure version check from firmware image header prevents subsequent download and flash write of
|
||||||
|
* entire firmware image. However this is optional because it is also taken care in API
|
||||||
|
* esp_https_ota_finish at the end of OTA update procedure.
|
||||||
|
*/
|
||||||
|
const uint32_t hw_sec_version = esp_efuse_read_secure_version();
|
||||||
|
if (new_app_info->secure_version < hw_sec_version)
|
||||||
|
{
|
||||||
|
ESP_LOGW(TAG, "New firmware security version is less than eFuse programmed, %d < %d", new_app_info->secure_version, hw_sec_version);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t http_client_set_header_cb(esp_http_client_handle_t http_client)
|
||||||
|
{
|
||||||
|
return esp_http_client_set_header(http_client, "Accept", "application/octet-stream");
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t _http_event_storage_handler(esp_http_client_event_t *evt)
|
||||||
|
{
|
||||||
|
static int output_pos;
|
||||||
|
static int last_progress;
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wswitch"
|
||||||
|
switch (evt->event_id)
|
||||||
|
{
|
||||||
|
case HTTP_EVENT_ON_CONNECTED:
|
||||||
|
{
|
||||||
|
output_pos = 0;
|
||||||
|
last_progress = 0;
|
||||||
|
/* Erase the Partition */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case HTTP_EVENT_ON_DATA:
|
||||||
|
if (!esp_http_client_is_chunked_response(evt->client))
|
||||||
|
{
|
||||||
|
Github_OTA_client_handle_t *handle = (Github_OTA_client_handle_t *)evt->user_data;
|
||||||
|
if (output_pos == 0)
|
||||||
|
{
|
||||||
|
ESP_LOGD(TAG, "Erasing Partition");
|
||||||
|
ESP_ERROR_CHECK(esp_partition_erase_range(handle->storage_partition, 0, handle->storage_partition->size));
|
||||||
|
ESP_LOGD(TAG, "Erasing Complete");
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_ERROR_CHECK(esp_partition_write(handle->storage_partition, output_pos, evt->data, evt->data_len));
|
||||||
|
output_pos += evt->data_len;
|
||||||
|
int progress = 100 * ((float)output_pos / (float)handle->storage_partition->size);
|
||||||
|
if ((progress % 5 == 0) && (progress != last_progress))
|
||||||
|
{
|
||||||
|
ESP_LOGV(TAG, "Storage Firmware Update Progress: %d%%", progress);
|
||||||
|
ESP_ERROR_CHECK(esp_event_post(Github_OTA_EVENTS, Github_OTA_EVENT_STORAGE_UPDATE_PROGRESS, &progress, sizeof(progress), portMAX_DELAY));
|
||||||
|
last_progress = progress;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case HTTP_EVENT_DISCONNECTED:
|
||||||
|
{
|
||||||
|
int mbedtls_err = 0;
|
||||||
|
esp_err_t err = esp_tls_get_and_clear_last_error(evt->data, &mbedtls_err, NULL);
|
||||||
|
if (err != 0)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "Last esp error code: 0x%x", err);
|
||||||
|
ESP_LOGE(TAG, "Last mbedtls failure: 0x%x", mbedtls_err);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t Github_OTA_storage_update(Github_OTA_client_handle_t *handle)
|
||||||
|
{
|
||||||
|
if (xSemaphoreTake(Github_OTA_lock, pdMS_TO_TICKS(1000)) != pdTRUE)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "Failed to take lock");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
if (handle == NULL)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "Invalid Handle");
|
||||||
|
xSemaphoreGive(Github_OTA_lock);
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
if (!strlen(handle->result.storageurl))
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "No Storage URL");
|
||||||
|
xSemaphoreGive(Github_OTA_lock);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
if (!strlen(handle->config.storagepartitionname))
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "No Storage Partition Name");
|
||||||
|
xSemaphoreGive(Github_OTA_lock);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
handle->storage_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, handle->config.storagepartitionname);
|
||||||
|
if (handle->storage_partition == NULL)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "Storage Partition Not Found");
|
||||||
|
xSemaphoreGive(Github_OTA_lock);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
ESP_LOGD(TAG, "Storage Partition %s - Type %x Subtype %x Found at %x - size %d", handle->storage_partition->label, handle->storage_partition->type, handle->storage_partition->subtype, handle->storage_partition->address, handle->storage_partition->size);
|
||||||
|
ESP_ERROR_CHECK(esp_event_post(Github_OTA_EVENTS, Github_OTA_EVENT_START_STORAGE_UPDATE, NULL, 0, portMAX_DELAY));
|
||||||
|
/* give time for the system to react, such as unmounting the filesystems etc */
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||||
|
|
||||||
|
esp_http_client_config_t config = {
|
||||||
|
.url = handle->result.storageurl,
|
||||||
|
.event_handler = _http_event_storage_handler,
|
||||||
|
.crt_bundle_attach = esp_crt_bundle_attach,
|
||||||
|
.user_data = handle,
|
||||||
|
.buffer_size_tx = 2048,
|
||||||
|
|
||||||
|
};
|
||||||
|
if (handle->username)
|
||||||
|
{
|
||||||
|
ESP_LOGD(TAG, "Using Authenticated Request to %s", config.url);
|
||||||
|
config.username = handle->username;
|
||||||
|
config.password = handle->token;
|
||||||
|
config.auth_type = HTTP_AUTH_TYPE_BASIC;
|
||||||
|
}
|
||||||
|
esp_http_client_handle_t client = esp_http_client_init(&config);
|
||||||
|
ESP_ERROR_CHECK(esp_http_client_set_header(client, "Accept", "application/octet-stream"));
|
||||||
|
esp_err_t err = esp_http_client_perform(client);
|
||||||
|
if (err == ESP_OK)
|
||||||
|
{
|
||||||
|
ESP_LOGD(TAG, "HTTP GET Status = %d, content_length = %d",
|
||||||
|
esp_http_client_get_status_code(client),
|
||||||
|
esp_http_client_get_content_length(client));
|
||||||
|
uint8_t sha256[32] = {0};
|
||||||
|
ESP_ERROR_CHECK(esp_partition_get_sha256(handle->storage_partition, sha256));
|
||||||
|
ESP_LOG_BUFFER_HEX("New Storage Partition SHA256:", sha256, sizeof(sha256));
|
||||||
|
ESP_ERROR_CHECK(esp_event_post(Github_OTA_EVENTS, Github_OTA_EVENT_FINISH_STORAGE_UPDATE, NULL, 0, portMAX_DELAY));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "HTTP GET request failed: %s", esp_err_to_name(err));
|
||||||
|
ESP_ERROR_CHECK(esp_event_post(Github_OTA_EVENTS, Github_OTA_EVENT_STORAGE_UPDATE_FAILED, NULL, 0, portMAX_DELAY));
|
||||||
|
}
|
||||||
|
esp_http_client_cleanup(client);
|
||||||
|
xSemaphoreGive(Github_OTA_lock);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t Github_OTA_update(Github_OTA_client_handle_t *handle)
|
||||||
|
{
|
||||||
|
esp_err_t ota_finish_err = ESP_OK;
|
||||||
|
if (xSemaphoreTake(Github_OTA_lock, pdMS_TO_TICKS(1000)) != pdTRUE)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "Failed to take lock");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
ESP_LOGI(TAG, "Scheduled Check for Firmware Update Starting");
|
||||||
|
ESP_ERROR_CHECK(esp_event_post(Github_OTA_EVENTS, Github_OTA_EVENT_START_UPDATE, NULL, 0, portMAX_DELAY));
|
||||||
|
if (!GetFlag(handle, Github_OTA_RELEASE_VALID_ASSET))
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "No Valid Release Asset Found");
|
||||||
|
ESP_ERROR_CHECK(esp_event_post(Github_OTA_EVENTS, Github_OTA_EVENT_UPDATE_FAILED, NULL, 0, portMAX_DELAY));
|
||||||
|
xSemaphoreGive(Github_OTA_lock);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
int cmp = semver_compare_version(handle->latest_version, handle->current_version);
|
||||||
|
if (cmp != 1)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "Current Version is equal or newer than new release");
|
||||||
|
ESP_ERROR_CHECK(esp_event_post(Github_OTA_EVENTS, Github_OTA_EVENT_UPDATE_FAILED, NULL, 0, portMAX_DELAY));
|
||||||
|
xSemaphoreGive(Github_OTA_lock);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
esp_http_client_config_t httpconfig = {
|
||||||
|
.url = handle->result.url,
|
||||||
|
.crt_bundle_attach = esp_crt_bundle_attach,
|
||||||
|
.keep_alive_enable = true,
|
||||||
|
.buffer_size_tx = 4096,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (handle->username)
|
||||||
|
{
|
||||||
|
ESP_LOGD(TAG, "Using Authenticated Request to %s", httpconfig.url);
|
||||||
|
httpconfig.username = handle->username;
|
||||||
|
httpconfig.password = handle->token;
|
||||||
|
httpconfig.auth_type = HTTP_AUTH_TYPE_BASIC;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_https_ota_config_t ota_config = {
|
||||||
|
.http_config = &httpconfig,
|
||||||
|
.http_client_init_cb = http_client_set_header_cb,
|
||||||
|
};
|
||||||
|
|
||||||
|
esp_https_ota_handle_t https_ota_handle = NULL;
|
||||||
|
esp_err_t err = esp_https_ota_begin(&ota_config, &https_ota_handle);
|
||||||
|
if (err != ESP_OK)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "ESP HTTPS OTA Begin failed: %d", err);
|
||||||
|
goto ota_end;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_app_desc_t app_desc;
|
||||||
|
err = esp_https_ota_get_img_desc(https_ota_handle, &app_desc);
|
||||||
|
if (err != ESP_OK)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "esp_https_ota_read_img_desc failed: %d", err);
|
||||||
|
goto ota_end;
|
||||||
|
}
|
||||||
|
err = validate_image_header(&app_desc);
|
||||||
|
if (err != ESP_OK)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "image header verification failed: %d", err);
|
||||||
|
goto ota_end;
|
||||||
|
}
|
||||||
|
int last_progress = -1;
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
err = esp_https_ota_perform(https_ota_handle);
|
||||||
|
if (err != ESP_ERR_HTTPS_OTA_IN_PROGRESS)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
int32_t dl = esp_https_ota_get_image_len_read(https_ota_handle);
|
||||||
|
int32_t size = esp_https_ota_get_image_size(https_ota_handle);
|
||||||
|
int progress = 100 * ((float)dl / (float)size);
|
||||||
|
if ((progress % 5 == 0) && (progress != last_progress))
|
||||||
|
{
|
||||||
|
ESP_ERROR_CHECK(esp_event_post(Github_OTA_EVENTS, Github_OTA_EVENT_FIRMWARE_UPDATE_PROGRESS, &progress, sizeof(progress), portMAX_DELAY));
|
||||||
|
ESP_LOGV(TAG, "Firmware Update Progress: %d%%", progress);
|
||||||
|
last_progress = progress;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (esp_https_ota_is_complete_data_received(https_ota_handle) != true)
|
||||||
|
{
|
||||||
|
// the OTA image was not completely received and user can customise the response to this situation.
|
||||||
|
ESP_LOGE(TAG, "Complete data was not received.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ota_finish_err = esp_https_ota_finish(https_ota_handle);
|
||||||
|
if ((err == ESP_OK) && (ota_finish_err == ESP_OK))
|
||||||
|
{
|
||||||
|
ESP_ERROR_CHECK(esp_event_post(Github_OTA_EVENTS, Github_OTA_EVENT_FINISH_UPDATE, NULL, 0, portMAX_DELAY));
|
||||||
|
if (strlen(handle->result.storageurl))
|
||||||
|
{
|
||||||
|
xSemaphoreGive(Github_OTA_lock);
|
||||||
|
if (Github_OTA_storage_update(handle) == ESP_OK)
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG, "Storage Update Successful");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "Storage Update Failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
xSemaphoreGive(Github_OTA_lock);
|
||||||
|
}
|
||||||
|
ESP_LOGI(TAG, "ESP_HTTPS_OTA upgrade successful. Rebooting ...");
|
||||||
|
ESP_ERROR_CHECK(esp_event_post(Github_OTA_EVENTS, Github_OTA_EVENT_PENDING_REBOOT, NULL, 0, portMAX_DELAY));
|
||||||
|
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||||
|
esp_restart();
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (ota_finish_err == ESP_ERR_OTA_VALIDATE_FAILED)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "Image validation failed, image is corrupted");
|
||||||
|
}
|
||||||
|
ESP_LOGE(TAG, "ESP_HTTPS_OTA upgrade failed 0x%x", ota_finish_err);
|
||||||
|
ESP_ERROR_CHECK(esp_event_post(Github_OTA_EVENTS, Github_OTA_EVENT_UPDATE_FAILED, NULL, 0, portMAX_DELAY));
|
||||||
|
xSemaphoreGive(Github_OTA_lock);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ota_end:
|
||||||
|
esp_https_ota_abort(https_ota_handle);
|
||||||
|
ESP_LOGE(TAG, "ESP_HTTPS_OTA upgrade failed");
|
||||||
|
ESP_ERROR_CHECK(esp_event_post(Github_OTA_EVENTS, Github_OTA_EVENT_UPDATE_FAILED, NULL, 0, portMAX_DELAY));
|
||||||
|
xSemaphoreGive(Github_OTA_lock);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
semver_t *Github_OTA_get_current_version(Github_OTA_client_handle_t *handle)
|
||||||
|
{
|
||||||
|
if (!handle)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
semver_t *cur = malloc(sizeof(semver_t));
|
||||||
|
memcpy(cur, &handle->current_version, sizeof(semver_t));
|
||||||
|
return cur;
|
||||||
|
}
|
||||||
|
|
||||||
|
semver_t *Github_OTA_get_latest_version(Github_OTA_client_handle_t *handle)
|
||||||
|
{
|
||||||
|
if (!handle)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (!GetFlag(handle, Github_OTA_RELEASE_VALID_ASSET))
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
semver_t *new = malloc(sizeof(semver_t));
|
||||||
|
memcpy(new, &handle->latest_version, sizeof(semver_t));
|
||||||
|
return new;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Github_OTA_task(void *pvParameters)
|
||||||
|
{
|
||||||
|
Github_OTA_client_handle_t *handle = (Github_OTA_client_handle_t *)pvParameters;
|
||||||
|
ESP_LOGI(TAG, "Firmware Update Task Starting");
|
||||||
|
if (handle)
|
||||||
|
{
|
||||||
|
if (Github_OTA_check(handle) == ESP_OK)
|
||||||
|
{
|
||||||
|
if (semver_gt(handle->latest_version, handle->current_version) == 1)
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG, "New Version Available");
|
||||||
|
Github_OTA_update(handle);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG, "No New Version Available");
|
||||||
|
ESP_ERROR_CHECK(esp_event_post(Github_OTA_EVENTS, Github_OTA_EVENT_NOUPDATE_AVAILABLE, NULL, 0, portMAX_DELAY));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG, "No Update Available");
|
||||||
|
ESP_ERROR_CHECK(esp_event_post(Github_OTA_EVENTS, Github_OTA_EVENT_NOUPDATE_AVAILABLE, NULL, 0, portMAX_DELAY));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ESP_LOGI(TAG, "Firmware Update Task Finished");
|
||||||
|
vTaskDelete(handle->task_handle);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||||
|
handle->task_handle = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t Github_OTA_start_update_task(Github_OTA_client_handle_t *handle)
|
||||||
|
{
|
||||||
|
if (!handle)
|
||||||
|
{
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
eTaskState state = eInvalid;
|
||||||
|
TaskHandle_t tmp = xTaskGetHandle("Github_OTA_task");
|
||||||
|
if (tmp)
|
||||||
|
{
|
||||||
|
state = eTaskGetState(tmp);
|
||||||
|
}
|
||||||
|
if (state == eDeleted || state == eInvalid)
|
||||||
|
{
|
||||||
|
ESP_LOGD(TAG, "Starting Task to Check for Updates");
|
||||||
|
if (xTaskCreate(Github_OTA_task, "Github_OTA_task", 6144, handle, 5, &handle->task_handle) != pdPASS)
|
||||||
|
{
|
||||||
|
ESP_LOGW(TAG, "Failed to Start Github_OTA_task");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ESP_LOGW(TAG, "Github_OTA_task Already Running");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Github_OTA_timer_callback(TimerHandle_t xTimer)
|
||||||
|
{
|
||||||
|
Github_OTA_client_handle_t *handle = (Github_OTA_client_handle_t *)pvTimerGetTimerID(xTimer);
|
||||||
|
if (handle)
|
||||||
|
{
|
||||||
|
handle->countdown--;
|
||||||
|
if (handle->countdown == 0)
|
||||||
|
{
|
||||||
|
handle->countdown = handle->config.updateInterval;
|
||||||
|
Github_OTA_start_update_task(handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t Github_OTA_start_update_timer(Github_OTA_client_handle_t *handle)
|
||||||
|
{
|
||||||
|
if (!handle)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "Failed to initialize Github_OTA Client");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
handle->countdown = handle->config.updateInterval;
|
||||||
|
|
||||||
|
/* run timer every minute */
|
||||||
|
uint64_t ticks = pdMS_TO_TICKS(1000) * 60;
|
||||||
|
TimerHandle_t timer = xTimerCreate("Github_OTA_timer", ticks, pdTRUE, (void *)handle, Github_OTA_timer_callback);
|
||||||
|
if (timer == NULL)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "Failed to create timer");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (xTimerStart(timer, 0) != pdPASS)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "Failed to start timer");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG, "Started Update Timer for %d Minutes", handle->config.updateInterval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *Github_OTA_get_event_str(Github_OTA_event_e event)
|
||||||
|
{
|
||||||
|
switch (event)
|
||||||
|
{
|
||||||
|
case Github_OTA_EVENT_START_CHECK:
|
||||||
|
return "Github_OTA_EVENT_START_CHECK";
|
||||||
|
case Github_OTA_EVENT_UPDATE_AVAILABLE:
|
||||||
|
return "Github_OTA_EVENT_UPDATE_AVAILABLE";
|
||||||
|
case Github_OTA_EVENT_NOUPDATE_AVAILABLE:
|
||||||
|
return "Github_OTA_EVENT_NOUPDATE_AVAILABLE";
|
||||||
|
case Github_OTA_EVENT_START_UPDATE:
|
||||||
|
return "Github_OTA_EVENT_START_UPDATE";
|
||||||
|
case Github_OTA_EVENT_FINISH_UPDATE:
|
||||||
|
return "Github_OTA_EVENT_FINISH_UPDATE";
|
||||||
|
case Github_OTA_EVENT_UPDATE_FAILED:
|
||||||
|
return "Github_OTA_EVENT_UPDATE_FAILED";
|
||||||
|
case Github_OTA_EVENT_START_STORAGE_UPDATE:
|
||||||
|
return "Github_OTA_EVENT_START_STORAGE_UPDATE";
|
||||||
|
case Github_OTA_EVENT_FINISH_STORAGE_UPDATE:
|
||||||
|
return "Github_OTA_EVENT_FINISH_STORAGE_UPDATE";
|
||||||
|
case Github_OTA_EVENT_STORAGE_UPDATE_FAILED:
|
||||||
|
return "Github_OTA_EVENT_STORAGE_UPDATE_FAILED";
|
||||||
|
case Github_OTA_EVENT_FIRMWARE_UPDATE_PROGRESS:
|
||||||
|
return "Github_OTA_EVENT_FIRMWARE_UPDATE_PROGRESS";
|
||||||
|
case Github_OTA_EVENT_STORAGE_UPDATE_PROGRESS:
|
||||||
|
return "Github_OTA_EVENT_STORAGE_UPDATE_PROGRESS";
|
||||||
|
case Github_OTA_EVENT_PENDING_REBOOT:
|
||||||
|
return "Github_OTA_EVENT_PENDING_REBOOT";
|
||||||
|
}
|
||||||
|
return "Unknown Event";
|
||||||
|
}
|
||||||
159
ESP/lib/src/network/OTA/Github_OTA/lib/esp_ota.h
Normal file
159
ESP/lib/src/network/OTA/Github_OTA/lib/esp_ota.h
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
#ifndef GITHUB_OTA_H
|
||||||
|
#define GITHUB_OTA_H
|
||||||
|
|
||||||
|
#include <esp_err.h>
|
||||||
|
#include <esp_event.h>
|
||||||
|
#include "SemVer/semver.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const char *TAG = "Github_OTA";
|
||||||
|
ESP_EVENT_DECLARE_BASE(Github_OTA_EVENTS);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Github OTA events
|
||||||
|
* These events are posted to the event loop to track progress of the OTA process
|
||||||
|
*/
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
Github_OTA_EVENT_START_CHECK = 0x01, /*!< Github OTA check started */
|
||||||
|
Github_OTA_EVENT_UPDATE_AVAILABLE = 0x02, /*!< Github OTA update available */
|
||||||
|
Github_OTA_EVENT_NOUPDATE_AVAILABLE = 0x04, /*!< Github OTA no update available */
|
||||||
|
Github_OTA_EVENT_START_UPDATE = 0x08, /*!< Github OTA update started */
|
||||||
|
Github_OTA_EVENT_FINISH_UPDATE = 0x10, /*!< Github OTA update finished */
|
||||||
|
Github_OTA_EVENT_UPDATE_FAILED = 0x20, /*!< Github OTA update failed */
|
||||||
|
Github_OTA_EVENT_START_STORAGE_UPDATE = 0x40, /*!< Github OTA storage update started. If the storage is mounted, you should unmount it when getting this call */
|
||||||
|
Github_OTA_EVENT_FINISH_STORAGE_UPDATE = 0x80, /*!< Github OTA storage update finished. You can mount the new storage after getting this call if needed */
|
||||||
|
Github_OTA_EVENT_STORAGE_UPDATE_FAILED = 0x100, /*!< Github OTA storage update failed */
|
||||||
|
Github_OTA_EVENT_FIRMWARE_UPDATE_PROGRESS = 0x200, /*!< Github OTA firmware update progress */
|
||||||
|
Github_OTA_EVENT_STORAGE_UPDATE_PROGRESS = 0x400, /*!< Github OTA storage update progress */
|
||||||
|
Github_OTA_EVENT_PENDING_REBOOT = 0x800, /*!< Github OTA pending reboot */
|
||||||
|
} Github_OTA_event_e;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Github OTA Configuration
|
||||||
|
*/
|
||||||
|
typedef struct Github_OTA_config_t
|
||||||
|
{
|
||||||
|
char *filenamematch; /*!< Filename to match against on Github indicating this is a firmware file */
|
||||||
|
char storagenamematch[CONFIG_MAX_FILENAME_LEN]; /*!< Filename to match against on Github indicating this is a storage file */
|
||||||
|
char storagepartitionname[17]; /*!< Name of the storage partition to update */
|
||||||
|
char *hostname; /*!< Hostname of the Github server. Defaults to api.github.com*/
|
||||||
|
char *orgname; /*!< Name of the Github organization */
|
||||||
|
char *reponame; /*!< Name of the Github repository */
|
||||||
|
uint32_t updateInterval; /*!< Interval in Minutes to check for updates if using the Github_OTA_start_update_timer function */
|
||||||
|
} Github_OTA_config_t;
|
||||||
|
|
||||||
|
typedef struct Github_OTA_client_handle_t Github_OTA_client_handle_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize the github ota client
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param config [in] Configuration for the github ota client
|
||||||
|
* @return Github_OTA_client_handle_t* handle to pass to all subsequent calls. If it returns NULL, there is a error in your config
|
||||||
|
*/
|
||||||
|
Github_OTA_client_handle_t *Github_OTA_init(Github_OTA_config_t *config);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the Username and Password to access private repositories or get more API calls
|
||||||
|
*
|
||||||
|
* Anonymus API calls are limited to 60 per hour. If you want to get more calls, you need to set a username and password.
|
||||||
|
* Be aware that this will be stored in the flash and can be read by anyone with access to the flash.
|
||||||
|
* The password should be a Github Personal Access Token and for good security you should limit what it can do
|
||||||
|
*
|
||||||
|
* @param handle the handle returned by Github_OTA_init
|
||||||
|
* @param username the username to authenticate with
|
||||||
|
* @param password this Github Personal Access Token
|
||||||
|
* @return esp_err_t ESP_OK if all is good, ESP_FAIL if there is an error
|
||||||
|
*/
|
||||||
|
esp_err_t Github_OTA_set_auth(Github_OTA_client_handle_t *handle, const char *username, const char *password);
|
||||||
|
/**
|
||||||
|
* @brief Free the Github_OTA client handle and all resources
|
||||||
|
*
|
||||||
|
* @param handle the Handle
|
||||||
|
* @return esp_err_t if there was a error
|
||||||
|
*/
|
||||||
|
esp_err_t Github_OTA_free(Github_OTA_client_handle_t *handle);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Perform a check for updates
|
||||||
|
*
|
||||||
|
* This will just check if there is a available update on Github releases with download resources that match your configuration
|
||||||
|
* for firmware and storage files. If it returns ESP_OK, you can call Github_OTA_get_latest_version to get the version of the latest release
|
||||||
|
*
|
||||||
|
* @param handle the Github_OTA_client_handle_t handle
|
||||||
|
* @return esp_err_t ESP_OK if there is a update available, ESP_FAIL if there is no update available or an error
|
||||||
|
*/
|
||||||
|
esp_err_t Github_OTA_check(Github_OTA_client_handle_t *handle);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Downloads and writes the latest firmware and storage partition (if available)
|
||||||
|
*
|
||||||
|
* You should only call this after calling Github_OTA_check and ensuring that there is a update available.
|
||||||
|
*
|
||||||
|
* @param handle the Github_OTA_client_handle_t handle
|
||||||
|
* @return esp_err_t ESP_FAIL if there is a error. If the Update is successful, it will not return, but reboot the device
|
||||||
|
*/
|
||||||
|
esp_err_t Github_OTA_update(Github_OTA_client_handle_t *handle);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the currently running version of the firmware
|
||||||
|
*
|
||||||
|
* This will return the version of the firmware currently running on your device.
|
||||||
|
* consult semver.h for functions to compare versions
|
||||||
|
*
|
||||||
|
* @param handle the Github_OTA_client_handle_t handle
|
||||||
|
* @return semver_t the version of the latest release
|
||||||
|
*/
|
||||||
|
|
||||||
|
semver_t *Github_OTA_get_current_version(Github_OTA_client_handle_t *handle);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the version of the latest release on Github. Only valid after calling Github_OTA_check
|
||||||
|
*
|
||||||
|
* @param handle the Github_OTA_client_handle_t handle
|
||||||
|
* @return semver_t* the version of the latest release on Github
|
||||||
|
*/
|
||||||
|
semver_t *Github_OTA_get_latest_version(Github_OTA_client_handle_t *handle);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Start a new Task that will check for updates and update if available
|
||||||
|
*
|
||||||
|
* This is equivalent to calling Github_OTA_check and Github_OTA_update if there is a new update available.
|
||||||
|
* If no update is available, it will not update the device.
|
||||||
|
*
|
||||||
|
* Progress can be monitored by registering for the Github_OTA_EVENTS events on the Global Event Loop
|
||||||
|
*
|
||||||
|
* @param handle Github_OTA_client_handle_t handle
|
||||||
|
* @return esp_err_t ESP_OK if the task was started, ESP_FAIL if there was an error
|
||||||
|
*/
|
||||||
|
esp_err_t Github_OTA_start_update_task(Github_OTA_client_handle_t *handle);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Install a Timer to automatically check for new updates and update if available
|
||||||
|
*
|
||||||
|
* Install a timer that will check for new updates every updateInterval seconds and update if available.
|
||||||
|
*
|
||||||
|
* @param handle Github_OTA_client_handle_t handle
|
||||||
|
* @return esp_err_t ESP_OK if no error, otherwise ESP_FAIL
|
||||||
|
*/
|
||||||
|
|
||||||
|
esp_err_t Github_OTA_start_update_timer(Github_OTA_client_handle_t *handle);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief convience function to return a string representation of events emited by this library
|
||||||
|
*
|
||||||
|
* @param event the eventid passed to the event handler
|
||||||
|
* @return char* a string representing the event
|
||||||
|
*/
|
||||||
|
const char *Github_OTA_get_event_str(Github_OTA_event_e event);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -1,6 +1,8 @@
|
|||||||
#include "OTA.hpp"
|
#include "OTA.hpp"
|
||||||
|
|
||||||
OTA::OTA(ProjectConfig *_deviceConfig) : _deviceConfig(_deviceConfig) {}
|
OTA::OTA(ProjectConfig *_deviceConfig) : _deviceConfig(_deviceConfig),
|
||||||
|
_bootTimestamp(0),
|
||||||
|
_isOtaEnabled(true) {}
|
||||||
|
|
||||||
OTA::~OTA() {}
|
OTA::~OTA() {}
|
||||||
|
|
||||||
@ -14,9 +16,7 @@ void OTA::SetupOTA()
|
|||||||
log_e("THE PASSWORD IS REQUIRED, [[ABORTING]]");
|
log_e("THE PASSWORD IS REQUIRED, [[ABORTING]]");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ArduinoOTA.setPort(localConfig->OTAPort);
|
ArduinoOTA.setPort(localConfig->OTAPort);
|
||||||
|
|
||||||
ArduinoOTA
|
ArduinoOTA
|
||||||
.onStart([]()
|
.onStart([]()
|
||||||
{
|
{
|
||||||
@ -50,7 +50,6 @@ void OTA::SetupOTA()
|
|||||||
log_e("End Failed");
|
log_e("End Failed");
|
||||||
break;
|
break;
|
||||||
} });
|
} });
|
||||||
|
|
||||||
log_i("Starting up basic OTA server");
|
log_i("Starting up basic OTA server");
|
||||||
log_i("OTA will be live for 5 minutes, after which it will be disabled until restart");
|
log_i("OTA will be live for 5 minutes, after which it will be disabled until restart");
|
||||||
auto mdnsConfig = _deviceConfig->getMDNSConfig();
|
auto mdnsConfig = _deviceConfig->getMDNSConfig();
|
||||||
|
|||||||
@ -13,8 +13,8 @@ public:
|
|||||||
void HandleOTAUpdate();
|
void HandleOTAUpdate();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
unsigned long _bootTimestamp = 0;
|
|
||||||
bool _isOtaEnabled = true;
|
|
||||||
ProjectConfig *_deviceConfig;
|
ProjectConfig *_deviceConfig;
|
||||||
|
unsigned long _bootTimestamp;
|
||||||
|
bool _isOtaEnabled;
|
||||||
};
|
};
|
||||||
#endif // OTA_HPP
|
#endif // OTA_HPP
|
||||||
@ -16,7 +16,8 @@ WiFiHandler::~WiFiHandler() {}
|
|||||||
|
|
||||||
void WiFiHandler::setupWifi()
|
void WiFiHandler::setupWifi()
|
||||||
{
|
{
|
||||||
if (this->_enable_adhoc || stateManager->getCurrentState() == WiFiState_e::WiFiState_ADHOC)
|
if (this->_enable_adhoc ||
|
||||||
|
stateManager->getCurrentState() == WiFiState_e::WiFiState_ADHOC)
|
||||||
{
|
{
|
||||||
this->setUpADHOC();
|
this->setUpADHOC();
|
||||||
return;
|
return;
|
||||||
@ -28,40 +29,53 @@ void WiFiHandler::setupWifi()
|
|||||||
log_i("Initializing connection to wifi \n\r");
|
log_i("Initializing connection to wifi \n\r");
|
||||||
stateManager->setState(WiFiState_e::WiFiState_Connecting);
|
stateManager->setState(WiFiState_e::WiFiState_Connecting);
|
||||||
|
|
||||||
std::vector<ProjectConfig::WiFiConfig_t> *networks = configManager->getWifiConfigs();
|
std::vector<ProjectConfig::WiFiConfig_t> *networks =
|
||||||
|
configManager->getWifiConfigs();
|
||||||
|
|
||||||
if (networks->empty())
|
if (networks->empty())
|
||||||
{
|
{
|
||||||
log_i("No networks found in config, trying the default one \n\r");
|
log_i("No networks found in config, trying the default one \n\r");
|
||||||
if (this->iniSTA(this->ssid.c_str(), this->password.c_str(), this->channel, (wifi_power_t)txpower->power))
|
if (this->iniSTA(this->ssid.c_str(), this->password.c_str(), this->channel,
|
||||||
|
(wifi_power_t)txpower->power))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
log_i("Could not connect to the hardcoded network, setting up ADHOC network \n\r");
|
log_i(
|
||||||
|
"Could not connect to the hardcoded network, setting up ADHOC "
|
||||||
|
"network \n\r");
|
||||||
this->setUpADHOC();
|
this->setUpADHOC();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto networkIterator = networks->begin(); networkIterator != networks->end(); ++networkIterator)
|
for (auto networkIterator = networks->begin();
|
||||||
|
networkIterator != networks->end(); ++networkIterator)
|
||||||
{
|
{
|
||||||
if (this->iniSTA(networkIterator->ssid.c_str(), networkIterator->password.c_str(), networkIterator->channel, (wifi_power_t)networkIterator->power))
|
if (this->iniSTA(
|
||||||
|
networkIterator->ssid.c_str(), networkIterator->password.c_str(),
|
||||||
|
networkIterator->channel, (wifi_power_t)networkIterator->power))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// at this point, we've tried every network, let's just setup adhoc
|
// at this point, we've tried every network, let's just setup adhoc
|
||||||
log_i("We've gone through every network, each timed out. Trying to connect to hardcoded network: %s \n\r", this->ssid.c_str());
|
log_i(
|
||||||
if (this->iniSTA(this->ssid.c_str(), this->password.c_str(), this->channel, (wifi_power_t)txpower->power))
|
"We've gone through every network, each timed out. Trying to connect "
|
||||||
|
"to hardcoded network: %s \n\r",
|
||||||
|
this->ssid.c_str());
|
||||||
|
if (this->iniSTA(this->ssid.c_str(), this->password.c_str(), this->channel,
|
||||||
|
(wifi_power_t)txpower->power))
|
||||||
{
|
{
|
||||||
log_i("Successfully connected to the hardcoded network. \n\r");
|
log_i("Successfully connected to the hardcoded network. \n\r");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
log_i("Could not connect to the hardcoded network, setting up adhoc. \n\r");
|
log_i(
|
||||||
|
"Could not connect to the hardcoded network, setting up adhoc. "
|
||||||
|
"\n\r");
|
||||||
this->setUpADHOC();
|
this->setUpADHOC();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -69,7 +83,7 @@ void WiFiHandler::setupWifi()
|
|||||||
void WiFiHandler::adhoc(const std::string &ssid, uint8_t channel, const std::string &password)
|
void WiFiHandler::adhoc(const std::string &ssid, uint8_t channel, const std::string &password)
|
||||||
{
|
{
|
||||||
stateManager->setState(WiFiState_e::WiFiState_ADHOC);
|
stateManager->setState(WiFiState_e::WiFiState_ADHOC);
|
||||||
|
|
||||||
log_i("\n[INFO]: Configuring access point...\n");
|
log_i("\n[INFO]: Configuring access point...\n");
|
||||||
WiFi.mode(WIFI_AP);
|
WiFi.mode(WIFI_AP);
|
||||||
WiFi.setSleep(WIFI_PS_NONE);
|
WiFi.setSleep(WIFI_PS_NONE);
|
||||||
@ -111,7 +125,8 @@ void WiFiHandler::setUpADHOC()
|
|||||||
log_d("\n[DEBUG]: channel: %d\n", configManager->getAPWifiConfig()->channel);
|
log_d("\n[DEBUG]: channel: %d\n", configManager->getAPWifiConfig()->channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WiFiHandler::iniSTA(const char *ssid, const char *password, uint8_t channel, wifi_power_t power)
|
bool WiFiHandler::iniSTA(const char *ssid, const char *password,
|
||||||
|
uint8_t channel, wifi_power_t power)
|
||||||
{
|
{
|
||||||
unsigned long currentMillis = millis();
|
unsigned long currentMillis = millis();
|
||||||
unsigned long startingMillis = currentMillis;
|
unsigned long startingMillis = currentMillis;
|
||||||
@ -122,7 +137,8 @@ bool WiFiHandler::iniSTA(const char *ssid, const char *password, uint8_t channel
|
|||||||
log_i("Trying to connect to: %s \n\r", ssid);
|
log_i("Trying to connect to: %s \n\r", ssid);
|
||||||
|
|
||||||
auto mdnsConfig = configManager->getMDNSConfig();
|
auto mdnsConfig = configManager->getMDNSConfig();
|
||||||
WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE, INADDR_NONE); // need to call before setting hostname
|
WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE,
|
||||||
|
INADDR_NONE); // need to call before setting hostname
|
||||||
WiFi.setHostname(mdnsConfig->hostname.c_str());
|
WiFi.setHostname(mdnsConfig->hostname.c_str());
|
||||||
WiFi.begin(ssid, password, channel);
|
WiFi.begin(ssid, password, channel);
|
||||||
while (WiFi.status() != WL_CONNECTED)
|
while (WiFi.status() != WL_CONNECTED)
|
||||||
|
|||||||
@ -14,10 +14,12 @@ BaseAPI::BaseAPI(int CONTROL_PORT,
|
|||||||
ProjectConfig *projectConfig,
|
ProjectConfig *projectConfig,
|
||||||
CameraHandler *camera,
|
CameraHandler *camera,
|
||||||
StateManager<WiFiState_e> *WiFiStateManager,
|
StateManager<WiFiState_e> *WiFiStateManager,
|
||||||
|
GithubOTAHandler *otaHandler,
|
||||||
const std::string &api_url) : server(new AsyncWebServer(CONTROL_PORT)),
|
const std::string &api_url) : server(new AsyncWebServer(CONTROL_PORT)),
|
||||||
projectConfig(projectConfig),
|
projectConfig(projectConfig),
|
||||||
camera(camera),
|
camera(camera),
|
||||||
WiFiStateManager(WiFiStateManager),
|
WiFiStateManager(WiFiStateManager),
|
||||||
|
otaHandler(otaHandler),
|
||||||
api_url(api_url) {}
|
api_url(api_url) {}
|
||||||
|
|
||||||
BaseAPI::~BaseAPI() {}
|
BaseAPI::~BaseAPI() {}
|
||||||
@ -180,6 +182,7 @@ void BaseAPI::setDeviceConfig(AsyncWebServerRequest *request)
|
|||||||
std::string hostname;
|
std::string hostname;
|
||||||
std::string service;
|
std::string service;
|
||||||
std::string ota_password;
|
std::string ota_password;
|
||||||
|
std::string firmware_name;
|
||||||
int ota_port;
|
int ota_port;
|
||||||
|
|
||||||
for (int i = 0; i < params; i++)
|
for (int i = 0; i < params; i++)
|
||||||
@ -187,11 +190,11 @@ void BaseAPI::setDeviceConfig(AsyncWebServerRequest *request)
|
|||||||
AsyncWebParameter *param = request->getParam(i);
|
AsyncWebParameter *param = request->getParam(i);
|
||||||
if (param->name() == "hostname")
|
if (param->name() == "hostname")
|
||||||
{
|
{
|
||||||
hostname = param->value().c_str();
|
hostname.assign(param->value().c_str());
|
||||||
}
|
}
|
||||||
else if (param->name() == "service")
|
else if (param->name() == "service")
|
||||||
{
|
{
|
||||||
service = param->value().c_str();
|
service.assign(param->value().c_str());
|
||||||
}
|
}
|
||||||
else if (param->name() == "ota_port")
|
else if (param->name() == "ota_port")
|
||||||
{
|
{
|
||||||
@ -199,11 +202,15 @@ void BaseAPI::setDeviceConfig(AsyncWebServerRequest *request)
|
|||||||
}
|
}
|
||||||
else if (param->name() == "ota_password")
|
else if (param->name() == "ota_password")
|
||||||
{
|
{
|
||||||
ota_password = param->value().c_str();
|
ota_password.assign(param->value().c_str());
|
||||||
|
}
|
||||||
|
else if (param->name() == "firmware_name")
|
||||||
|
{
|
||||||
|
firmware_name.assign(param->value().c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// note: We're passing empty params by design, this is done to reset specific fields
|
// note: We're passing empty params by design, this is done to reset specific fields
|
||||||
projectConfig->setDeviceConfig(ota_password, &ota_port, true);
|
projectConfig->setDeviceConfig(ota_password, ota_port, firmware_name, true);
|
||||||
projectConfig->setMDNSConfig(hostname, service, true);
|
projectConfig->setMDNSConfig(hostname, service, true);
|
||||||
request->send(200, MIMETYPE_JSON, "{\"msg\":\"Done. Device Config has been set.\"}");
|
request->send(200, MIMETYPE_JSON, "{\"msg\":\"Done. Device Config has been set.\"}");
|
||||||
}
|
}
|
||||||
@ -289,6 +296,26 @@ void BaseAPI::factoryReset(AsyncWebServerRequest *request)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BaseAPI::initOTAHandler(AsyncWebServerRequest *request)
|
||||||
|
{
|
||||||
|
switch (_networkMethodsMap_enum[request->method()])
|
||||||
|
{
|
||||||
|
case POST:
|
||||||
|
{
|
||||||
|
log_d("OTA Update Initiated");
|
||||||
|
auto otaConfig = projectConfig->getDeviceConfig();
|
||||||
|
// start the OTA update
|
||||||
|
otaHandler->updateFirmware(otaConfig->binaryName);
|
||||||
|
request->send(200, MIMETYPE_JSON, "{\"msg\":\"OTA Update In progress\"}");
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
request->send(400, MIMETYPE_JSON, "{\"msg\":\"Invalid Request\"}");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//*********************************************************************************************
|
//*********************************************************************************************
|
||||||
//! Camera Command Functions
|
//! Camera Command Functions
|
||||||
//*********************************************************************************************
|
//*********************************************************************************************
|
||||||
|
|||||||
@ -15,13 +15,14 @@
|
|||||||
#define XHTTP_OPTIONS 0b01000000;
|
#define XHTTP_OPTIONS 0b01000000;
|
||||||
#define XHTTP_ANY 0b01111111; */
|
#define XHTTP_ANY 0b01111111; */
|
||||||
|
|
||||||
|
#include "network/OTA/Github_OTA/GithubOTAHandler.hpp"
|
||||||
#define HTTP_ANY 0b01111111
|
#define HTTP_ANY 0b01111111
|
||||||
#define HTTP_GET 0b00000001
|
#define HTTP_GET 0b00000001
|
||||||
|
|
||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
#include <AsyncTCP.h>
|
#include <AsyncTCP.h>
|
||||||
|
|
||||||
//#include "network/api/utilities/apiUtilities.hpp" //! Only needed for the shaEncoder function (for now)
|
// #include "network/api/utilities/apiUtilities.hpp" //! Only needed for the shaEncoder function (for now)
|
||||||
#include "data/utilities/network_utilities.hpp"
|
#include "data/utilities/network_utilities.hpp"
|
||||||
|
|
||||||
#include "data/config/project_config.hpp"
|
#include "data/config/project_config.hpp"
|
||||||
@ -47,6 +48,7 @@ protected:
|
|||||||
void setWiFiTXPower(AsyncWebServerRequest *request);
|
void setWiFiTXPower(AsyncWebServerRequest *request);
|
||||||
void getJsonConfig(AsyncWebServerRequest *request);
|
void getJsonConfig(AsyncWebServerRequest *request);
|
||||||
void factoryReset(AsyncWebServerRequest *request);
|
void factoryReset(AsyncWebServerRequest *request);
|
||||||
|
void initOTAHandler(AsyncWebServerRequest *request);
|
||||||
void setDeviceConfig(AsyncWebServerRequest *request);
|
void setDeviceConfig(AsyncWebServerRequest *request);
|
||||||
void rebootDevice(AsyncWebServerRequest *request);
|
void rebootDevice(AsyncWebServerRequest *request);
|
||||||
void ping(AsyncWebServerRequest *request);
|
void ping(AsyncWebServerRequest *request);
|
||||||
@ -98,13 +100,15 @@ protected:
|
|||||||
AsyncWebServer *server;
|
AsyncWebServer *server;
|
||||||
CameraHandler *camera;
|
CameraHandler *camera;
|
||||||
StateManager<WiFiState_e> *WiFiStateManager;
|
StateManager<WiFiState_e> *WiFiStateManager;
|
||||||
|
GithubOTAHandler *otaHandler;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
BaseAPI(int CONTROL_PORT,
|
BaseAPI(int CONTROL_PORT,
|
||||||
ProjectConfig *projectConfig,
|
ProjectConfig *projectConfig,
|
||||||
CameraHandler *camera,
|
CameraHandler *camera,
|
||||||
StateManager<WiFiState_e> *WiFiStateManager,
|
StateManager<WiFiState_e> *WiFiStateManager,
|
||||||
const std::string &api_url);
|
GithubOTAHandler *otaHandler,
|
||||||
|
const std::string &api_url);
|
||||||
|
|
||||||
virtual ~BaseAPI();
|
virtual ~BaseAPI();
|
||||||
virtual void begin();
|
virtual void begin();
|
||||||
|
|||||||
@ -8,10 +8,12 @@ APIServer::APIServer(int CONTROL_PORT,
|
|||||||
ProjectConfig *projectConfig,
|
ProjectConfig *projectConfig,
|
||||||
CameraHandler *camera,
|
CameraHandler *camera,
|
||||||
StateManager<WiFiState_e> *WiFiStateManager,
|
StateManager<WiFiState_e> *WiFiStateManager,
|
||||||
|
GithubOTAHandler *otaHandler,
|
||||||
const std::string &api_url) : BaseAPI(CONTROL_PORT,
|
const std::string &api_url) : BaseAPI(CONTROL_PORT,
|
||||||
projectConfig,
|
projectConfig,
|
||||||
camera,
|
camera,
|
||||||
WiFiStateManager,
|
WiFiStateManager,
|
||||||
|
otaHandler,
|
||||||
api_url) {}
|
api_url) {}
|
||||||
|
|
||||||
APIServer::~APIServer() {}
|
APIServer::~APIServer() {}
|
||||||
|
|||||||
@ -8,10 +8,11 @@ class APIServer : public BaseAPI
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
APIServer(int CONTROL_PORT,
|
APIServer(int CONTROL_PORT,
|
||||||
ProjectConfig *projectConfig,
|
ProjectConfig *projectConfig,
|
||||||
CameraHandler *camera,
|
CameraHandler *camera,
|
||||||
StateManager<WiFiState_e> *WiFiStateManager,
|
StateManager<WiFiState_e> *WiFiStateManager,
|
||||||
const std::string &api_url);
|
GithubOTAHandler *otaHandler,
|
||||||
|
const std::string &api_url);
|
||||||
|
|
||||||
virtual ~APIServer();
|
virtual ~APIServer();
|
||||||
void begin();
|
void begin();
|
||||||
|
|||||||
107
ESP/lib/src/network/mDNS/auto/AutoDiscovery.cpp
Normal file
107
ESP/lib/src/network/mDNS/auto/AutoDiscovery.cpp
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
#include "AutoDiscovery.hpp"
|
||||||
|
|
||||||
|
AutoDiscovery::AutoDiscovery(StateManager<MDNSState_e> *stateManager,
|
||||||
|
ProjectConfig *configManager) : stateManager(stateManager),
|
||||||
|
configManager(configManager),
|
||||||
|
mdnsConfig(NULL),
|
||||||
|
_queryTimeout(5000),
|
||||||
|
_currentTime(millis()),
|
||||||
|
_previousTime(0)
|
||||||
|
{
|
||||||
|
if (mdns_init() != ESP_OK)
|
||||||
|
{
|
||||||
|
stateManager->setState(MDNSState_e::MDNSState_Error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mdnsConfig = configManager->getMDNSConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
AutoDiscovery::~AutoDiscovery()
|
||||||
|
{
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutoDiscovery::start()
|
||||||
|
{
|
||||||
|
stateManager->setState(MDNSState_e::MDNSState_QueryStarted);
|
||||||
|
queryService(mdnsConfig->hostname.c_str(), "tcp");
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Query for the number of services on the network
|
||||||
|
*
|
||||||
|
* @param service string - the number of services found
|
||||||
|
* @param protocol string - the protocol of the service
|
||||||
|
* @return int - the number of services found
|
||||||
|
*/
|
||||||
|
void AutoDiscovery::queryService(const std::string &service, const std::string &protocol)
|
||||||
|
{
|
||||||
|
if (stateManager->getCurrentState() != MDNSState_e::MDNSState_QueryComplete)
|
||||||
|
{
|
||||||
|
int numServices = 0;
|
||||||
|
if (_currentTime - _previousTime >= _queryTimeout)
|
||||||
|
{
|
||||||
|
_previousTime = _currentTime;
|
||||||
|
int n = MDNS.queryService(service.c_str(), protocol.c_str());
|
||||||
|
if (n != numServices)
|
||||||
|
{
|
||||||
|
numServices = n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (numServices == 0)
|
||||||
|
{
|
||||||
|
log_w("no services found");
|
||||||
|
// set the hostname for mDNS - this is what we will need to connect to
|
||||||
|
if (mdnsConfig->hostname != "openiristracker")
|
||||||
|
{
|
||||||
|
log_w("No hostname set, using default");
|
||||||
|
configManager->setMDNSConfig(MDNS_HOSTNAME, mdnsConfig->hostname.c_str(), true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
log_i("Setting hostname to %s", mdnsConfig->hostname.c_str());
|
||||||
|
configManager->setMDNSConfig(mdnsConfig->hostname.c_str(), mdnsConfig->service.c_str(), true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_i("%d services found", numServices);
|
||||||
|
for (int i = 0; i < numServices; ++i)
|
||||||
|
{
|
||||||
|
// print details for each service found
|
||||||
|
log_i("service #%d: %s - %s:%d", i + 1, MDNS.hostname(i).c_str(), MDNS.IP(i).toString().c_str(), MDNS.port(i));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::string iterator = std::to_string(numServices);
|
||||||
|
std::string hostname = mdnsConfig->hostname.assign(iterator);
|
||||||
|
|
||||||
|
if (mdnsConfig->hostname != "openiristracker")
|
||||||
|
{
|
||||||
|
log_w("No hostname set, using default");
|
||||||
|
configManager->setMDNSConfig(MDNS_HOSTNAME, mdnsConfig->hostname.c_str(), true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
log_i("Setting hostname to %s", hostname.c_str());
|
||||||
|
configManager->setMDNSConfig(hostname.c_str(), mdnsConfig->service.c_str(), true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutoDiscovery::stop()
|
||||||
|
{
|
||||||
|
stateManager->setState(MDNSState_e::MDNSState_QueryComplete);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutoDiscovery::update(ObserverEvent::Event event)
|
||||||
|
{
|
||||||
|
switch (event)
|
||||||
|
{
|
||||||
|
case ObserverEvent::Event::mdnsConfigUpdated:
|
||||||
|
MDNS.end();
|
||||||
|
start();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Path: lib\src\network\mDNS\auto\AutoDiscovery.hpp
|
||||||
32
ESP/lib/src/network/mDNS/auto/AutoDiscovery.hpp
Normal file
32
ESP/lib/src/network/mDNS/auto/AutoDiscovery.hpp
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#ifndef AutoDiscovery_hpp
|
||||||
|
#define AutoDiscovery_hpp
|
||||||
|
|
||||||
|
#include <ESPmDNS.h>
|
||||||
|
#include <WiFi.h>
|
||||||
|
#include "data/StateManager/StateManager.hpp"
|
||||||
|
#include "data/utilities/Observer.hpp"
|
||||||
|
#include "data/utilities/helpers.hpp"
|
||||||
|
#include "data/config/project_config.hpp"
|
||||||
|
|
||||||
|
class AutoDiscovery : public IObserver
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
StateManager<MDNSState_e> *stateManager;
|
||||||
|
ProjectConfig *configManager;
|
||||||
|
ProjectConfig::MDNSConfig_t *mdnsConfig;
|
||||||
|
int _queryTimeout;
|
||||||
|
unsigned long _currentTime;
|
||||||
|
unsigned long _previousTime;
|
||||||
|
|
||||||
|
void queryService(const std::string &service, const std::string &protocol);
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
public:
|
||||||
|
AutoDiscovery(StateManager<MDNSState_e> *stateManager,
|
||||||
|
ProjectConfig *configManager);
|
||||||
|
~AutoDiscovery();
|
||||||
|
void start();
|
||||||
|
void update(ObserverEvent::Event event);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // AutoDiscovery_hpp
|
||||||
8
ESP/partitions.csv
Normal file
8
ESP/partitions.csv
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# Name, Type, SubType, Offset, Size, Flags
|
||||||
|
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
|
||||||
|
nvs, data, nvs, 0x9000, 0x6000,
|
||||||
|
phy_init, data, phy, 0xf000, 0x1000,
|
||||||
|
otadata, data, ota, , 0x2000
|
||||||
|
ota_0, app, ota_0, , 1M,
|
||||||
|
ota_1, app, ota_1, , 1M
|
||||||
|
storage, data, spiffs, , 0xF0000,
|
||||||
|
@ -11,220 +11,215 @@
|
|||||||
[platformio]
|
[platformio]
|
||||||
default_envs = esp32AITinker ; do not change this value unless you know what you are doing
|
default_envs = esp32AITinker ; do not change this value unless you know what you are doing
|
||||||
|
|
||||||
; The below options are available for all environments
|
|
||||||
; The ssid and password are requried for the trackers to connect to your network!!!
|
|
||||||
[wifi]
|
[wifi]
|
||||||
ssid="" ; your wifi network name goes here
|
ssid = ""
|
||||||
password="" ; your wifi network password goes here
|
password = ""
|
||||||
mDNSName="openiristracker" ; the name of the tracker as it will appear on your network
|
mdnsname = "openiristracker"
|
||||||
channel=1 ; wifi channel
|
channel = 1
|
||||||
ap_ssid="EyeTrackVR" ; your AP wifi network name goes here
|
ap_ssid = "EyeTrackVR"
|
||||||
ap_password="test" ; Place your AP Wifi password here
|
ap_password = "test"
|
||||||
OTAPassword="12345678"
|
otapassword = "12345678"
|
||||||
OTAServerPort=3232
|
otaserverport = 3232
|
||||||
enableADHOC=0 ; 0 = disable, 1 = enable
|
enableadhoc = 0
|
||||||
adhocChannel=1 ; channel to use for adhoc network
|
adhocchannel = 1
|
||||||
enableOTA=1 ; 0 = disable, 1 = enable
|
enableota = 1
|
||||||
|
|
||||||
; DO NOT EDIT BELOW THIS LINE UNLESS YOU KNOW WHAT YOU ARE DOING
|
|
||||||
[pinoutsESPCAM_M5STACK]
|
|
||||||
; M5-Stack camera
|
|
||||||
# WARNING: The M5-Stack camera does not have PSRAM please set the noPSRM flag to 1
|
|
||||||
PWDN_GPIO_NUM = -1
|
|
||||||
RESET_GPIO_NUM = 15
|
|
||||||
XCLK_GPIO_NUM = 27
|
|
||||||
SIOD_GPIO_NUM = 25
|
|
||||||
SIOC_GPIO_NUM = 23
|
|
||||||
Y9_GPIO_NUM = 19
|
|
||||||
Y8_GPIO_NUM = 36
|
|
||||||
Y7_GPIO_NUM = 18
|
|
||||||
Y6_GPIO_NUM = 39
|
|
||||||
Y5_GPIO_NUM = 5
|
|
||||||
Y4_GPIO_NUM = 34
|
|
||||||
Y3_GPIO_NUM = 35
|
|
||||||
Y2_GPIO_NUM = 17
|
|
||||||
VSYNC_GPIO_NUM = 22
|
|
||||||
HREF_GPIO_NUM = 26
|
|
||||||
PCLK_GPIO_NUM = 21
|
|
||||||
|
|
||||||
|
[ESP_GITHUB_OTA]
|
||||||
|
maxfilenamelen = 64
|
||||||
|
maxurllen = 128
|
||||||
|
githubhostname = "api.github.com"
|
||||||
|
githubowner = "lorow"
|
||||||
|
githubrepo = "OpenIris"
|
||||||
build_flags =
|
build_flags =
|
||||||
; CAMERA PINOUT DEFINITIONS
|
-DCONFIG_MAX_FILENAME_LEN=${ESP_GITHUB_OTA.maxFilenameLen}
|
||||||
-DPWDN_GPIO_NUM=${pinoutsESPCAM_M5STACK.PWDN_GPIO_NUM} ; Set the PWDN pin
|
-DCONFIG_MAX_URL_LEN=${ESP_GITHUB_OTA.maxURLLen}
|
||||||
-DRESET_GPIO_NUM=${pinoutsESPCAM_M5STACK.RESET_GPIO_NUM} ; Set the RESET pin
|
'-DCONFIG_GITHUB_HOSTNAME=${ESP_GITHUB_OTA.githubHostname}'
|
||||||
-DXCLK_GPIO_NUM=${pinoutsESPCAM_M5STACK.XCLK_GPIO_NUM} ; Set the XCLK pin
|
'-DCONFIG_GITHUB_OWNER=${ESP_GITHUB_OTA.githubOwner}'
|
||||||
-DSIOD_GPIO_NUM=${pinoutsESPCAM_M5STACK.SIOD_GPIO_NUM} ; Set the SIOD pin
|
'-DCONFIG_GITHUB_REPO=${ESP_GITHUB_OTA.githubRepo}'
|
||||||
-DSIOC_GPIO_NUM=${pinoutsESPCAM_M5STACK.SIOC_GPIO_NUM} ; Set the SIOC pin
|
|
||||||
-DY9_GPIO_NUM=${pinoutsESPCAM_M5STACK.Y9_GPIO_NUM} ; Set the Y9 pin
|
|
||||||
-DY8_GPIO_NUM=${pinoutsESPCAM_M5STACK.Y8_GPIO_NUM} ; Set the Y8 pin
|
|
||||||
-DY7_GPIO_NUM=${pinoutsESPCAM_M5STACK.Y7_GPIO_NUM} ; Set the Y7 pin
|
|
||||||
-DY6_GPIO_NUM=${pinoutsESPCAM_M5STACK.Y6_GPIO_NUM} ; Set the Y6 pin
|
|
||||||
-DY5_GPIO_NUM=${pinoutsESPCAM_M5STACK.Y5_GPIO_NUM} ; Set the Y5 pin
|
|
||||||
-DY4_GPIO_NUM=${pinoutsESPCAM_M5STACK.Y4_GPIO_NUM} ; Set the Y4 pin
|
|
||||||
-DY3_GPIO_NUM=${pinoutsESPCAM_M5STACK.Y3_GPIO_NUM} ; Set the Y3 pin
|
|
||||||
-DY2_GPIO_NUM=${pinoutsESPCAM_M5STACK.Y2_GPIO_NUM} ; Set the Y2 pin
|
|
||||||
-DVSYNC_GPIO_NUM=${pinoutsESPCAM_M5STACK.VSYNC_GPIO_NUM} ; Set the VSYNC pin
|
|
||||||
-DHREF_GPIO_NUM=${pinoutsESPCAM_M5STACK.HREF_GPIO_NUM} ; Set the HREF pin
|
|
||||||
-DPCLK_GPIO_NUM=${pinoutsESPCAM_M5STACK.PCLK_GPIO_NUM} ; Set the PCLK pin
|
|
||||||
|
|
||||||
[pinoutsESPCAM]
|
[pinoutsESPCAM_M5STACK]
|
||||||
; ESP32CAM, the ov2640
|
pwdn_gpio_num = -1
|
||||||
PWDN_GPIO_NUM = 32
|
reset_gpio_num = 15
|
||||||
RESET_GPIO_NUM = 33
|
xclk_gpio_num = 27
|
||||||
XCLK_GPIO_NUM = 4
|
siod_gpio_num = 25
|
||||||
SIOD_GPIO_NUM = 18
|
sioc_gpio_num = 23
|
||||||
SIOC_GPIO_NUM = 23
|
y9_gpio_num = 19
|
||||||
Y9_GPIO_NUM = 36
|
y8_gpio_num = 36
|
||||||
Y8_GPIO_NUM = 19
|
y7_gpio_num = 18
|
||||||
Y7_GPIO_NUM = 21
|
y6_gpio_num = 39
|
||||||
Y6_GPIO_NUM = 39
|
y5_gpio_num = 5
|
||||||
Y5_GPIO_NUM = 35
|
y4_gpio_num = 34
|
||||||
Y4_GPIO_NUM = 14
|
y3_gpio_num = 35
|
||||||
Y3_GPIO_NUM = 13
|
y2_gpio_num = 17
|
||||||
Y2_GPIO_NUM = 34
|
vsync_gpio_num = 22
|
||||||
VSYNC_GPIO_NUM = 5
|
href_gpio_num = 26
|
||||||
HREF_GPIO_NUM = 27
|
pclk_gpio_num = 21
|
||||||
PCLK_GPIO_NUM = 25
|
build_flags =
|
||||||
|
'-DCAMERA_MODULE_NAME="ESP32_CAM_M5STACK"'
|
||||||
|
-DPWDN_GPIO_NUM=${pinoutsESPCAM_M5STACK.PWDN_GPIO_NUM}
|
||||||
|
-DRESET_GPIO_NUM=${pinoutsESPCAM_M5STACK.RESET_GPIO_NUM}
|
||||||
|
-DXCLK_GPIO_NUM=${pinoutsESPCAM_M5STACK.XCLK_GPIO_NUM}
|
||||||
|
-DSIOD_GPIO_NUM=${pinoutsESPCAM_M5STACK.SIOD_GPIO_NUM}
|
||||||
|
-DSIOC_GPIO_NUM=${pinoutsESPCAM_M5STACK.SIOC_GPIO_NUM}
|
||||||
|
-DY9_GPIO_NUM=${pinoutsESPCAM_M5STACK.Y9_GPIO_NUM}
|
||||||
|
-DY8_GPIO_NUM=${pinoutsESPCAM_M5STACK.Y8_GPIO_NUM}
|
||||||
|
-DY7_GPIO_NUM=${pinoutsESPCAM_M5STACK.Y7_GPIO_NUM}
|
||||||
|
-DY6_GPIO_NUM=${pinoutsESPCAM_M5STACK.Y6_GPIO_NUM}
|
||||||
|
-DY5_GPIO_NUM=${pinoutsESPCAM_M5STACK.Y5_GPIO_NUM}
|
||||||
|
-DY4_GPIO_NUM=${pinoutsESPCAM_M5STACK.Y4_GPIO_NUM}
|
||||||
|
-DY3_GPIO_NUM=${pinoutsESPCAM_M5STACK.Y3_GPIO_NUM}
|
||||||
|
-DY2_GPIO_NUM=${pinoutsESPCAM_M5STACK.Y2_GPIO_NUM}
|
||||||
|
-DVSYNC_GPIO_NUM=${pinoutsESPCAM_M5STACK.VSYNC_GPIO_NUM}
|
||||||
|
-DHREF_GPIO_NUM=${pinoutsESPCAM_M5STACK.HREF_GPIO_NUM}
|
||||||
|
-DPCLK_GPIO_NUM=${pinoutsESPCAM_M5STACK.PCLK_GPIO_NUM}
|
||||||
|
|
||||||
build_flags =
|
[pinoutsESPCam]
|
||||||
|
pwdn_gpio_num = 32
|
||||||
|
reset_gpio_num = 33
|
||||||
|
xclk_gpio_num = 4
|
||||||
|
siod_gpio_num = 18
|
||||||
|
sioc_gpio_num = 23
|
||||||
|
y9_gpio_num = 36
|
||||||
|
y8_gpio_num = 19
|
||||||
|
y7_gpio_num = 21
|
||||||
|
y6_gpio_num = 39
|
||||||
|
y5_gpio_num = 35
|
||||||
|
y4_gpio_num = 14
|
||||||
|
y3_gpio_num = 13
|
||||||
|
y2_gpio_num = 34
|
||||||
|
vsync_gpio_num = 5
|
||||||
|
href_gpio_num = 27
|
||||||
|
pclk_gpio_num = 25
|
||||||
|
build_flags =
|
||||||
'-DCAMERA_MODULE_NAME="ESP32_CAM"'
|
'-DCAMERA_MODULE_NAME="ESP32_CAM"'
|
||||||
-DCONFIG_CAMERA_MODULE_CAM_BOARD=1
|
-DCONFIG_CAMERA_MODULE_CAM_BOARD=1
|
||||||
; CAMERA PINOUT DEFINITIONS
|
-DPWDN_GPIO_NUM=${pinoutsESPCam.PWDN_GPIO_NUM}
|
||||||
-DPWDN_GPIO_NUM=${pinoutsESPCAM.PWDN_GPIO_NUM} ; Set the PWDN pin
|
-DRESET_GPIO_NUM=${pinoutsESPCam.RESET_GPIO_NUM}
|
||||||
-DRESET_GPIO_NUM=${pinoutsESPCAM.RESET_GPIO_NUM} ; Set the RESET pin
|
-DXCLK_GPIO_NUM=${pinoutsESPCam.XCLK_GPIO_NUM}
|
||||||
-DXCLK_GPIO_NUM=${pinoutsESPCAM.XCLK_GPIO_NUM} ; Set the XCLK pin
|
-DSIOD_GPIO_NUM=${pinoutsESPCam.SIOD_GPIO_NUM}
|
||||||
-DSIOD_GPIO_NUM=${pinoutsESPCAM.SIOD_GPIO_NUM} ; Set the SIOD pin
|
-DSIOC_GPIO_NUM=${pinoutsESPCam.SIOC_GPIO_NUM}
|
||||||
-DSIOC_GPIO_NUM=${pinoutsESPCAM.SIOC_GPIO_NUM} ; Set the SIOC pin
|
-DY9_GPIO_NUM=${pinoutsESPCam.Y9_GPIO_NUM}
|
||||||
-DY9_GPIO_NUM=${pinoutsESPCAM.Y9_GPIO_NUM} ; Set the Y9 pin
|
-DY8_GPIO_NUM=${pinoutsESPCam.Y8_GPIO_NUM}
|
||||||
-DY8_GPIO_NUM=${pinoutsESPCAM.Y8_GPIO_NUM} ; Set the Y8 pin
|
-DY7_GPIO_NUM=${pinoutsESPCam.Y7_GPIO_NUM}
|
||||||
-DY7_GPIO_NUM=${pinoutsESPCAM.Y7_GPIO_NUM} ; Set the Y7 pin
|
-DY6_GPIO_NUM=${pinoutsESPCam.Y6_GPIO_NUM}
|
||||||
-DY6_GPIO_NUM=${pinoutsESPCAM.Y6_GPIO_NUM} ; Set the Y6 pin
|
-DY5_GPIO_NUM=${pinoutsESPCam.Y5_GPIO_NUM}
|
||||||
-DY5_GPIO_NUM=${pinoutsESPCAM.Y5_GPIO_NUM} ; Set the Y5 pin
|
-DY4_GPIO_NUM=${pinoutsESPCam.Y4_GPIO_NUM}
|
||||||
-DY4_GPIO_NUM=${pinoutsESPCAM.Y4_GPIO_NUM} ; Set the Y4 pin
|
-DY3_GPIO_NUM=${pinoutsESPCam.Y3_GPIO_NUM}
|
||||||
-DY3_GPIO_NUM=${pinoutsESPCAM.Y3_GPIO_NUM} ; Set the Y3 pin
|
-DY2_GPIO_NUM=${pinoutsESPCam.Y2_GPIO_NUM}
|
||||||
-DY2_GPIO_NUM=${pinoutsESPCAM.Y2_GPIO_NUM} ; Set the Y2 pin
|
-DVSYNC_GPIO_NUM=${pinoutsESPCam.VSYNC_GPIO_NUM}
|
||||||
-DVSYNC_GPIO_NUM=${pinoutsESPCAM.VSYNC_GPIO_NUM} ; Set the VSYNC pin
|
-DHREF_GPIO_NUM=${pinoutsESPCam.HREF_GPIO_NUM}
|
||||||
-DHREF_GPIO_NUM=${pinoutsESPCAM.HREF_GPIO_NUM} ; Set the HREF pin
|
-DPCLK_GPIO_NUM=${pinoutsESPCam.PCLK_GPIO_NUM}
|
||||||
-DPCLK_GPIO_NUM=${pinoutsESPCAM.PCLK_GPIO_NUM} ; Set the PCLK pin
|
|
||||||
|
|
||||||
[pinoutsAITinker]
|
[pinoutsAITinker]
|
||||||
; AI Tinker camera, the ov2650
|
pwdn_gpio_num = 32
|
||||||
PWDN_GPIO_NUM = 32
|
reset_gpio_num = -1
|
||||||
RESET_GPIO_NUM = -1
|
xclk_gpio_num = 0
|
||||||
XCLK_GPIO_NUM = 0
|
siod_gpio_num = 26
|
||||||
SIOD_GPIO_NUM = 26
|
sioc_gpio_num = 27
|
||||||
SIOC_GPIO_NUM = 27
|
y9_gpio_num = 35
|
||||||
Y9_GPIO_NUM = 35
|
y8_gpio_num = 34
|
||||||
Y8_GPIO_NUM = 34
|
y7_gpio_num = 39
|
||||||
Y7_GPIO_NUM = 39
|
y6_gpio_num = 36
|
||||||
Y6_GPIO_NUM = 36
|
y5_gpio_num = 21
|
||||||
Y5_GPIO_NUM = 21
|
y4_gpio_num = 19
|
||||||
Y4_GPIO_NUM = 19
|
y3_gpio_num = 18
|
||||||
Y3_GPIO_NUM = 18
|
y2_gpio_num = 5
|
||||||
Y2_GPIO_NUM = 5
|
vsync_gpio_num = 25
|
||||||
VSYNC_GPIO_NUM = 25
|
href_gpio_num = 23
|
||||||
HREF_GPIO_NUM = 23
|
pclk_gpio_num = 22
|
||||||
PCLK_GPIO_NUM = 22
|
build_flags =
|
||||||
|
|
||||||
build_flags =
|
|
||||||
'-DCAMERA_MODULE_NAME="ESP32_CAM_AI_THINKER"'
|
'-DCAMERA_MODULE_NAME="ESP32_CAM_AI_THINKER"'
|
||||||
-DCONFIG_CAMERA_MODULE_AITINKER_BOARD=1
|
-DCONFIG_CAMERA_MODULE_AITINKER_BOARD=1
|
||||||
; CAMERA PINOUT DEFINITIONS
|
-DPWDN_GPIO_NUM=${pinoutsAITinker.PWDN_GPIO_NUM}
|
||||||
-DPWDN_GPIO_NUM=${pinoutsAITinker.PWDN_GPIO_NUM} ; Set the PWDN pin
|
-DRESET_GPIO_NUM=${pinoutsAITinker.RESET_GPIO_NUM}
|
||||||
-DRESET_GPIO_NUM=${pinoutsAITinker.RESET_GPIO_NUM} ; Set the RESET pin
|
-DXCLK_GPIO_NUM=${pinoutsAITinker.XCLK_GPIO_NUM}
|
||||||
-DXCLK_GPIO_NUM=${pinoutsAITinker.XCLK_GPIO_NUM} ; Set the XCLK pin
|
-DSIOD_GPIO_NUM=${pinoutsAITinker.SIOD_GPIO_NUM}
|
||||||
-DSIOD_GPIO_NUM=${pinoutsAITinker.SIOD_GPIO_NUM} ; Set the SIOD pin
|
-DSIOC_GPIO_NUM=${pinoutsAITinker.SIOC_GPIO_NUM}
|
||||||
-DSIOC_GPIO_NUM=${pinoutsAITinker.SIOC_GPIO_NUM} ; Set the SIOC pin
|
-DY9_GPIO_NUM=${pinoutsAITinker.Y9_GPIO_NUM}
|
||||||
-DY9_GPIO_NUM=${pinoutsAITinker.Y9_GPIO_NUM} ; Set the Y9 pin
|
-DY8_GPIO_NUM=${pinoutsAITinker.Y8_GPIO_NUM}
|
||||||
-DY8_GPIO_NUM=${pinoutsAITinker.Y8_GPIO_NUM} ; Set the Y8 pin
|
-DY7_GPIO_NUM=${pinoutsAITinker.Y7_GPIO_NUM}
|
||||||
-DY7_GPIO_NUM=${pinoutsAITinker.Y7_GPIO_NUM} ; Set the Y7 pin
|
-DY6_GPIO_NUM=${pinoutsAITinker.Y6_GPIO_NUM}
|
||||||
-DY6_GPIO_NUM=${pinoutsAITinker.Y6_GPIO_NUM} ; Set the Y6 pin
|
-DY5_GPIO_NUM=${pinoutsAITinker.Y5_GPIO_NUM}
|
||||||
-DY5_GPIO_NUM=${pinoutsAITinker.Y5_GPIO_NUM} ; Set the Y5 pin
|
-DY4_GPIO_NUM=${pinoutsAITinker.Y4_GPIO_NUM}
|
||||||
-DY4_GPIO_NUM=${pinoutsAITinker.Y4_GPIO_NUM} ; Set the Y4 pin
|
-DY3_GPIO_NUM=${pinoutsAITinker.Y3_GPIO_NUM}
|
||||||
-DY3_GPIO_NUM=${pinoutsAITinker.Y3_GPIO_NUM} ; Set the Y3 pin
|
-DY2_GPIO_NUM=${pinoutsAITinker.Y2_GPIO_NUM}
|
||||||
-DY2_GPIO_NUM=${pinoutsAITinker.Y2_GPIO_NUM} ; Set the Y2 pin
|
-DVSYNC_GPIO_NUM=${pinoutsAITinker.VSYNC_GPIO_NUM}
|
||||||
-DVSYNC_GPIO_NUM=${pinoutsAITinker.VSYNC_GPIO_NUM} ; Set the VSYNC pin
|
-DHREF_GPIO_NUM=${pinoutsAITinker.HREF_GPIO_NUM}
|
||||||
-DHREF_GPIO_NUM=${pinoutsAITinker.HREF_GPIO_NUM} ; Set the HREF pin
|
-DPCLK_GPIO_NUM=${pinoutsAITinker.PCLK_GPIO_NUM}
|
||||||
-DPCLK_GPIO_NUM=${pinoutsAITinker.PCLK_GPIO_NUM} ; Set the PCLK pin
|
|
||||||
|
|
||||||
[pinoutsESP-EYE]
|
[pinoutsESP-EYE]
|
||||||
; CAMERA_MODULE_ESP-EYE_KIT
|
pwdn_gpio_num = -1
|
||||||
PWDN_GPIO_NUM = -1
|
reset_gpio_num = -1
|
||||||
RESET_GPIO_NUM = -1
|
xclk_gpio_num = 4
|
||||||
XCLK_GPIO_NUM = 4
|
siod_gpio_num = 18
|
||||||
SIOD_GPIO_NUM = 18
|
sioc_gpio_num = 23
|
||||||
SIOC_GPIO_NUM = 23
|
y9_gpio_num = 36
|
||||||
Y9_GPIO_NUM = 36
|
y8_gpio_num = 37
|
||||||
Y8_GPIO_NUM = 37
|
y7_gpio_num = 38
|
||||||
Y7_GPIO_NUM = 38
|
y6_gpio_num = 39
|
||||||
Y6_GPIO_NUM = 39
|
y5_gpio_num = 14
|
||||||
Y5_GPIO_NUM = 35
|
y4_gpio_num = 19
|
||||||
Y4_GPIO_NUM = 14
|
y3_gpio_num = 13
|
||||||
Y3_GPIO_NUM = 13
|
y2_gpio_num = 34
|
||||||
Y2_GPIO_NUM = 34
|
vsync_gpio_num = 5
|
||||||
VSYNC_GPIO_NUM = 5
|
href_gpio_num = 27
|
||||||
HREF_GPIO_NUM = 27
|
pclk_gpio_num = 25
|
||||||
PCLK_GPIO_NUM = 25
|
|
||||||
|
|
||||||
build_flags =
|
build_flags =
|
||||||
'-DCAMERA_MODULE_NAME="ESP32_CAM_ESP_EYE"'
|
'-DCAMERA_MODULE_NAME="ESP32_CAM_ESP_EYE"'
|
||||||
-DCONFIG_CAMERA_MODULE_ESP_EYE=1
|
-DCONFIG_CAMERA_MODULE_ESP_EYE=1
|
||||||
; CAMERA PINOUT DEFINITIONS
|
-DPWDN_GPIO_NUM=${pinoutsESP-EYE.PWDN_GPIO_NUM}
|
||||||
-DPWDN_GPIO_NUM=${pinoutsESP-EYE.PWDN_GPIO_NUM} ; Set the PWDN pin
|
-DRESET_GPIO_NUM=${pinoutsESP-EYE.RESET_GPIO_NUM}
|
||||||
-DRESET_GPIO_NUM=${pinoutsESP-EYE.RESET_GPIO_NUM} ; Set the RESET pin
|
-DXCLK_GPIO_NUM=${pinoutsESP-EYE.XCLK_GPIO_NUM}
|
||||||
-DXCLK_GPIO_NUM=${pinoutsESP-EYE.XCLK_GPIO_NUM} ; Set the XCLK pin
|
-DSIOD_GPIO_NUM=${pinoutsESP-EYE.SIOD_GPIO_NUM}
|
||||||
-DSIOD_GPIO_NUM=${pinoutsESP-EYE.SIOD_GPIO_NUM} ; Set the SIOD pin
|
-DSIOC_GPIO_NUM=${pinoutsESP-EYE.SIOC_GPIO_NUM}
|
||||||
-DSIOC_GPIO_NUM=${pinoutsESP-EYE.SIOC_GPIO_NUM} ; Set the SIOC pin
|
-DY9_GPIO_NUM=${pinoutsESP-EYE.Y9_GPIO_NUM}
|
||||||
-DY9_GPIO_NUM=${pinoutsESP-EYE.Y9_GPIO_NUM} ; Set the Y9 pin
|
-DY8_GPIO_NUM=${pinoutsESP-EYE.Y8_GPIO_NUM}
|
||||||
-DY8_GPIO_NUM=${pinoutsESP-EYE.Y8_GPIO_NUM} ; Set the Y8 pin
|
-DY7_GPIO_NUM=${pinoutsESP-EYE.Y7_GPIO_NUM}
|
||||||
-DY7_GPIO_NUM=${pinoutsESP-EYE.Y7_GPIO_NUM} ; Set the Y7 pin
|
-DY6_GPIO_NUM=${pinoutsESP-EYE.Y6_GPIO_NUM}
|
||||||
-DY6_GPIO_NUM=${pinoutsESP-EYE.Y6_GPIO_NUM} ; Set the Y6 pin
|
-DY5_GPIO_NUM=${pinoutsESP-EYE.Y5_GPIO_NUM}
|
||||||
-DY5_GPIO_NUM=${pinoutsESP-EYE.Y5_GPIO_NUM} ; Set the Y5 pin
|
-DY4_GPIO_NUM=${pinoutsESP-EYE.Y4_GPIO_NUM}
|
||||||
-DY4_GPIO_NUM=${pinoutsESP-EYE.Y4_GPIO_NUM} ; Set the Y4 pin
|
-DY3_GPIO_NUM=${pinoutsESP-EYE.Y3_GPIO_NUM}
|
||||||
-DY3_GPIO_NUM=${pinoutsESP-EYE.Y3_GPIO_NUM} ; Set the Y3 pin
|
-DY2_GPIO_NUM=${pinoutsESP-EYE.Y2_GPIO_NUM}
|
||||||
-DY2_GPIO_NUM=${pinoutsESP-EYE.Y2_GPIO_NUM} ; Set the Y2 pin
|
-DVSYNC_GPIO_NUM=${pinoutsESP-EYE.VSYNC_GPIO_NUM}
|
||||||
-DVSYNC_GPIO_NUM=${pinoutsESP-EYE.VSYNC_GPIO_NUM} ; Set the VSYNC pin
|
-DHREF_GPIO_NUM=${pinoutsESP-EYE.HREF_GPIO_NUM}
|
||||||
-DHREF_GPIO_NUM=${pinoutsESP-EYE.HREF_GPIO_NUM} ; Set the HREF pin
|
-DPCLK_GPIO_NUM=${pinoutsESP-EYE.PCLK_GPIO_NUM}
|
||||||
-DPCLK_GPIO_NUM=${pinoutsESP-EYE.PCLK_GPIO_NUM} ; Set the PCLK pin
|
|
||||||
|
|
||||||
[pinoutsESPWROVER]
|
[pinoutsESPWROVER]
|
||||||
; CAMERA_MODULE_WROVER_KIT
|
pwdn_gpio_num = -1
|
||||||
PWDN_GPIO_NUM = -1
|
reset_gpio_num = -1
|
||||||
RESET_GPIO_NUM = -1
|
xclk_gpio_num = 21
|
||||||
XCLK_GPIO_NUM = 21
|
siod_gpio_num = 26
|
||||||
SIOD_GPIO_NUM = 26
|
sioc_gpio_num = 27
|
||||||
SIOC_GPIO_NUM = 27
|
y9_gpio_num = 35
|
||||||
Y9_GPIO_NUM = 35
|
y8_gpio_num = 34
|
||||||
Y8_GPIO_NUM = 34
|
y7_gpio_num = 39
|
||||||
Y7_GPIO_NUM = 39
|
y6_gpio_num = 36
|
||||||
Y6_GPIO_NUM = 36
|
y5_gpio_num = 19
|
||||||
Y5_GPIO_NUM = 19
|
y4_gpio_num = 18
|
||||||
Y4_GPIO_NUM = 18
|
y3_gpio_num = 5
|
||||||
Y3_GPIO_NUM = 5
|
y2_gpio_num = 4
|
||||||
Y2_GPIO_NUM = 4
|
vsync_gpio_num = 25
|
||||||
VSYNC_GPIO_NUM = 25
|
href_gpio_num = 23
|
||||||
HREF_GPIO_NUM = 23
|
pclk_gpio_num = 22
|
||||||
PCLK_GPIO_NUM = 22
|
|
||||||
|
|
||||||
build_flags =
|
build_flags =
|
||||||
'-DCAMERA_MODULE_NAME="ESP32_CAM_WROVER_KIT"'
|
'-DCAMERA_MODULE_NAME="ESP32_CAM_WROVER_KIT"'
|
||||||
-DCONFIG_CAMERA_MODULE_WROVER_KIT=1
|
-DCONFIG_CAMERA_MODULE_WROVER_KIT=1
|
||||||
; CAMERA PINOUT DEFINITIONS
|
-DPWDN_GPIO_NUM=${pinoutsESPWROVER.PWDN_GPIO_NUM}
|
||||||
-DPWDN_GPIO_NUM=${pinoutsESPWROVER.PWDN_GPIO_NUM} ; Set the PWDN pin
|
-DRESET_GPIO_NUM=${pinoutsESPWROVER.RESET_GPIO_NUM}
|
||||||
-DRESET_GPIO_NUM=${pinoutsESPWROVER.RESET_GPIO_NUM} ; Set the RESET pin
|
-DXCLK_GPIO_NUM=${pinoutsESPWROVER.XCLK_GPIO_NUM}
|
||||||
-DXCLK_GPIO_NUM=${pinoutsESPWROVER.XCLK_GPIO_NUM} ; Set the XCLK pin
|
-DSIOD_GPIO_NUM=${pinoutsESPWROVER.SIOD_GPIO_NUM}
|
||||||
-DSIOD_GPIO_NUM=${pinoutsESPWROVER.SIOD_GPIO_NUM} ; Set the SIOD pin
|
-DSIOC_GPIO_NUM=${pinoutsESPWROVER.SIOC_GPIO_NUM}
|
||||||
-DSIOC_GPIO_NUM=${pinoutsESPWROVER.SIOC_GPIO_NUM} ; Set the SIOC pin
|
-DY9_GPIO_NUM=${pinoutsESPWROVER.Y9_GPIO_NUM}
|
||||||
-DY9_GPIO_NUM=${pinoutsESPWROVER.Y9_GPIO_NUM} ; Set the Y9 pin
|
-DY8_GPIO_NUM=${pinoutsESPWROVER.Y8_GPIO_NUM}
|
||||||
-DY8_GPIO_NUM=${pinoutsESPWROVER.Y8_GPIO_NUM} ; Set the Y8 pin
|
-DY7_GPIO_NUM=${pinoutsESPWROVER.Y7_GPIO_NUM}
|
||||||
-DY7_GPIO_NUM=${pinoutsESPWROVER.Y7_GPIO_NUM} ; Set the Y7 pin
|
-DY6_GPIO_NUM=${pinoutsESPWROVER.Y6_GPIO_NUM}
|
||||||
-DY6_GPIO_NUM=${pinoutsESPWROVER.Y6_GPIO_NUM} ; Set the Y6 pin
|
-DY5_GPIO_NUM=${pinoutsESPWROVER.Y5_GPIO_NUM}
|
||||||
-DY5_GPIO_NUM=${pinoutsESPWROVER.Y5_GPIO_NUM} ; Set the Y5 pin
|
-DY4_GPIO_NUM=${pinoutsESPWROVER.Y4_GPIO_NUM}
|
||||||
-DY4_GPIO_NUM=${pinoutsESPWROVER.Y4_GPIO_NUM} ; Set the Y4 pin
|
-DY3_GPIO_NUM=${pinoutsESPWROVER.Y3_GPIO_NUM}
|
||||||
-DY3_GPIO_NUM=${pinoutsESPWROVER.Y3_GPIO_NUM} ; Set the Y3 pin
|
-DY2_GPIO_NUM=${pinoutsESPWROVER.Y2_GPIO_NUM}
|
||||||
-DY2_GPIO_NUM=${pinoutsESPWROVER.Y2_GPIO_NUM} ; Set the Y2 pin
|
-DVSYNC_GPIO_NUM=${pinoutsESPWROVER.VSYNC_GPIO_NUM}
|
||||||
-DVSYNC_GPIO_NUM=${pinoutsESPWROVER.VSYNC_GPIO_NUM} ; Set the VSYNC pin
|
-DHREF_GPIO_NUM=${pinoutsESPWROVER.HREF_GPIO_NUM}
|
||||||
-DHREF_GPIO_NUM=${pinoutsESPWROVER.HREF_GPIO_NUM} ; Set the HREF pin
|
-DPCLK_GPIO_NUM=${pinoutsESPWROVER.PCLK_GPIO_NUM}
|
||||||
-DPCLK_GPIO_NUM=${pinoutsESPWROVER.PCLK_GPIO_NUM} ; Set the PCLK pin
|
|
||||||
|
|
||||||
[common]
|
[common]
|
||||||
platform = espressif32
|
platform = espressif32
|
||||||
@ -233,19 +228,16 @@ monitor_speed = 115200
|
|||||||
monitor_rts = 0
|
monitor_rts = 0
|
||||||
monitor_dtr = 0
|
monitor_dtr = 0
|
||||||
monitor_filters =
|
monitor_filters =
|
||||||
;colorize -- uncomment this to get a colorful text in your terminal
|
|
||||||
log2file
|
log2file
|
||||||
time
|
time
|
||||||
default
|
default
|
||||||
esp32_exception_decoder
|
esp32_exception_decoder
|
||||||
|
board_build.partitions = min_spiffs.csv
|
||||||
;board_build.partitions = ${common.board_build.partitions} ; uncomment this to use the huge_app partition table, does not support OTA and only 1MB of SPIFFS
|
|
||||||
board_build.partitions = min_spiffs.csv ; uncomment this to use the min_spiffs partition table, supports OTA and 1MB of SPIFFS
|
|
||||||
|
|
||||||
build_flags =
|
build_flags =
|
||||||
!python tools/\autoversioning.py ; add build version to build as preprocessor defines
|
${ESP_GITHUB_OTA.build_flags}
|
||||||
!python tools/\git_rev.py ; add git revision to build as preprocessor defines
|
!python tools/autoversioning.py
|
||||||
-DOTA_SERVER_PORT=${wifi.OTAServerPort} ; Set the OTA server
|
!python tools/git_rev.py
|
||||||
|
-DOTA_SERVER_PORT=${wifi.OTAServerPort}
|
||||||
-DENABLE_ADHOC=${wifi.enableADHOC}
|
-DENABLE_ADHOC=${wifi.enableADHOC}
|
||||||
-DADHOC_CHANNEL=${wifi.adhocChannel}
|
-DADHOC_CHANNEL=${wifi.adhocChannel}
|
||||||
-DWIFI_CHANNEL=${wifi.channel}
|
-DWIFI_CHANNEL=${wifi.channel}
|
||||||
@ -270,10 +262,9 @@ upload_speed = 115200 ;115200 is used for compatability - if you are on windows
|
|||||||
lib_deps =
|
lib_deps =
|
||||||
;esp32-camera
|
;esp32-camera
|
||||||
leftcoast/LC_baseTools@^1.5
|
leftcoast/LC_baseTools@^1.5
|
||||||
; geeksville/Micro-RTSP @ ^0.1.6 ; Micro-RTSP library for streaming video over RTSP - will be implemented soon
|
bblanchon/ArduinoJson@^6.19.4
|
||||||
https://github.com/me-no-dev/ESPAsyncWebServer.git
|
https://github.com/me-no-dev/ESPAsyncWebServer.git
|
||||||
https://github.com/me-no-dev/AsyncTCP.git
|
https://github.com/me-no-dev/AsyncTCP.git
|
||||||
https://github.com/bblanchon/ArduinoJson.git
|
|
||||||
|
|
||||||
build_type = debug
|
build_type = debug
|
||||||
extra_scripts = pre:tools/customname.py
|
extra_scripts = pre:tools/customname.py
|
||||||
@ -286,11 +277,11 @@ monitor_speed = ${common.monitor_speed}
|
|||||||
monitor_rts = ${common.monitor_rts}
|
monitor_rts = ${common.monitor_rts}
|
||||||
monitor_dtr = ${common.monitor_dtr}
|
monitor_dtr = ${common.monitor_dtr}
|
||||||
monitor_filters = ${common.monitor_filters}
|
monitor_filters = ${common.monitor_filters}
|
||||||
;build_unflags = ${common.build_unflags}
|
board_build.partitions = ${common.board_build.partitions}
|
||||||
board_build.partitions = ${common.board_build.partitions} ;
|
|
||||||
lib_ldf_mode = ${common.lib_ldf_mode}
|
lib_ldf_mode = ${common.lib_ldf_mode}
|
||||||
upload_speed = ${common.upload_speed}
|
upload_speed = ${common.upload_speed}
|
||||||
lib_deps = ${common.lib_deps}
|
lib_deps =
|
||||||
|
${common.lib_deps}
|
||||||
build_type = ${common.build_type}
|
build_type = ${common.build_type}
|
||||||
extra_scripts = ${common.extra_scripts}
|
extra_scripts = ${common.extra_scripts}
|
||||||
build_flags = ${common.build_flags}
|
build_flags = ${common.build_flags}
|
||||||
@ -306,11 +297,11 @@ framework = ${common.framework}
|
|||||||
monitor_speed = ${common.monitor_speed}
|
monitor_speed = ${common.monitor_speed}
|
||||||
monitor_rts = ${common.monitor_rts}
|
monitor_rts = ${common.monitor_rts}
|
||||||
monitor_dtr = ${common.monitor_dtr}
|
monitor_dtr = ${common.monitor_dtr}
|
||||||
;build_unflags = ${common.build_unflags}
|
board_build.partitions = ${common.board_build.partitions}
|
||||||
board_build.partitions = ${common.board_build.partitions} ;
|
|
||||||
lib_ldf_mode = ${common.lib_ldf_mode}
|
lib_ldf_mode = ${common.lib_ldf_mode}
|
||||||
upload_speed = ${common.upload_speed}
|
upload_speed = ${common.upload_speed}
|
||||||
lib_deps = ${common.lib_deps}
|
lib_deps =
|
||||||
|
${common.lib_deps}
|
||||||
build_type = release
|
build_type = release
|
||||||
extra_scripts = ${common.extra_scripts}
|
extra_scripts = ${common.extra_scripts}
|
||||||
build_flags = ${common.build_flags}
|
build_flags = ${common.build_flags}
|
||||||
@ -336,8 +327,129 @@ upload_speed = ${common.upload_speed}
|
|||||||
monitor_speed = ${common.monitor_speed}
|
monitor_speed = ${common.monitor_speed}
|
||||||
monitor_rts = ${common.monitor_rts}
|
monitor_rts = ${common.monitor_rts}
|
||||||
monitor_dtr = ${common.monitor_dtr}
|
monitor_dtr = ${common.monitor_dtr}
|
||||||
;build_unflags = ${common.build_unflags}
|
board_build.partitions = ${common.board_build.partitions}
|
||||||
board_build.partitions = ${common.board_build.partitions} ; uncomment this to use the min_spiffs partition table, great for using OTA
|
lib_ldf_mode = ${common.lib_ldf_mode}
|
||||||
|
upload_port = 192.168.1.38
|
||||||
|
upload_protocol = espota
|
||||||
|
upload_flags =
|
||||||
|
--port=3232
|
||||||
|
--auth=12345678
|
||||||
|
build_type = release
|
||||||
|
extra_scripts = ${common.extra_scripts}
|
||||||
|
|
||||||
|
[env:esp_eye]
|
||||||
|
platform = ${common.platform}
|
||||||
|
board = esp32cam
|
||||||
|
framework = ${common.framework}
|
||||||
|
monitor_speed = ${common.monitor_speed}
|
||||||
|
monitor_filters = ${common.monitor_filters}
|
||||||
|
board_build.partitions = ${common.board_build.partitions}
|
||||||
|
lib_ldf_mode = ${common.lib_ldf_mode}
|
||||||
|
upload_speed = ${common.upload_speed}
|
||||||
|
lib_deps =
|
||||||
|
${common.lib_deps}
|
||||||
|
build_type = ${common.build_type}
|
||||||
|
extra_scripts = ${common.extra_scripts}
|
||||||
|
build_flags = ${common.build_flags}
|
||||||
|
-DDEBUG_MODE=1
|
||||||
|
-DCORE_DEBUG_LEVEL=4
|
||||||
|
${pinoutsESP-EYE.build_flags}
|
||||||
|
|
||||||
|
[env:esp_eye_release]
|
||||||
|
platform = ${common.platform}
|
||||||
|
board = esp32cam
|
||||||
|
framework = ${common.framework}
|
||||||
|
monitor_speed = ${common.monitor_speed}
|
||||||
|
board_build.partitions = ${common.board_build.partitions}
|
||||||
|
lib_ldf_mode = ${common.lib_ldf_mode}
|
||||||
|
upload_speed = ${common.upload_speed}
|
||||||
|
lib_deps =
|
||||||
|
${common.lib_deps}
|
||||||
|
build_type = release
|
||||||
|
extra_scripts = ${common.extra_scripts}
|
||||||
|
build_flags = ${common.build_flags}
|
||||||
|
-DDEBUG_MODE=0
|
||||||
|
-DCORE_DEBUG_LEVEL=1
|
||||||
|
${pinoutsESP-EYE.build_flags}
|
||||||
|
|
||||||
|
[env:esp_eye_OTA]
|
||||||
|
platform = ${common.platform}
|
||||||
|
board = esp32cam
|
||||||
|
framework = ${common.framework}
|
||||||
|
build_flags = ${common.build_flags}
|
||||||
|
-DDEBUG_MODE=0
|
||||||
|
-DCORE_DEBUG_LEVEL=1
|
||||||
|
-DDEBUG_ESP_OTA
|
||||||
|
${pinoutsESP-EYE.build_flags}
|
||||||
|
lib_deps =
|
||||||
|
${common.lib_deps}
|
||||||
|
upload_speed = ${common.upload_speed}
|
||||||
|
monitor_speed = ${common.monitor_speed}
|
||||||
|
board_build.partitions = ${common.board_build.partitions}
|
||||||
|
lib_ldf_mode = ${common.lib_ldf_mode}
|
||||||
|
upload_port = 192.168.1.38
|
||||||
|
upload_protocol = espota
|
||||||
|
upload_flags =
|
||||||
|
--port=3232
|
||||||
|
--auth=12345678
|
||||||
|
build_type = release
|
||||||
|
extra_scripts = ${common.extra_scripts}
|
||||||
|
|
||||||
|
[env:esp32M5Stack]
|
||||||
|
platform = ${common.platform}
|
||||||
|
board = esp32cam
|
||||||
|
framework = ${common.framework}
|
||||||
|
monitor_speed = ${common.monitor_speed}
|
||||||
|
monitor_rts = ${common.monitor_rts}
|
||||||
|
monitor_dtr = ${common.monitor_dtr}
|
||||||
|
monitor_filters = ${common.monitor_filters}
|
||||||
|
board_build.partitions = ${common.board_build.partitions}
|
||||||
|
lib_ldf_mode = ${common.lib_ldf_mode}
|
||||||
|
upload_speed = ${common.upload_speed}
|
||||||
|
lib_deps =
|
||||||
|
${common.lib_deps}
|
||||||
|
build_type = ${common.build_type}
|
||||||
|
extra_scripts = ${common.extra_scripts}
|
||||||
|
build_flags = ${common.build_flags}
|
||||||
|
-DDEBUG_MODE=1
|
||||||
|
-DCORE_DEBUG_LEVEL=4
|
||||||
|
${pinoutsESPCAM_M5STACK.build_flags}
|
||||||
|
|
||||||
|
[env:esp32M5Stack_release]
|
||||||
|
platform = ${common.platform}
|
||||||
|
board = esp32cam
|
||||||
|
framework = ${common.framework}
|
||||||
|
monitor_speed = ${common.monitor_speed}
|
||||||
|
monitor_rts = ${common.monitor_rts}
|
||||||
|
monitor_dtr = ${common.monitor_dtr}
|
||||||
|
board_build.partitions = ${common.board_build.partitions}
|
||||||
|
lib_ldf_mode = ${common.lib_ldf_mode}
|
||||||
|
upload_speed = ${common.upload_speed}
|
||||||
|
lib_deps =
|
||||||
|
${common.lib_deps}
|
||||||
|
build_type = release
|
||||||
|
extra_scripts = ${common.extra_scripts}
|
||||||
|
build_flags = ${common.build_flags}
|
||||||
|
-DDEBUG_MODE=0
|
||||||
|
-DCORE_DEBUG_LEVEL=1
|
||||||
|
${pinoutsESPCAM_M5STACK.build_flags}
|
||||||
|
|
||||||
|
[env:esp32M5Stack_OTA]
|
||||||
|
platform = ${common.platform}
|
||||||
|
board = esp32cam
|
||||||
|
framework = ${common.framework}
|
||||||
|
build_flags = ${common.build_flags}
|
||||||
|
-DDEBUG_MODE=0
|
||||||
|
-DCORE_DEBUG_LEVEL=1
|
||||||
|
-DDEBUG_ESP_OTA
|
||||||
|
${pinoutsESPCAM_M5STACK.build_flags}
|
||||||
|
lib_deps =
|
||||||
|
${common.lib_deps}
|
||||||
|
upload_speed = ${common.upload_speed}
|
||||||
|
monitor_speed = ${common.monitor_speed}
|
||||||
|
monitor_rts = ${common.monitor_rts}
|
||||||
|
monitor_dtr = ${common.monitor_dtr}
|
||||||
|
board_build.partitions = ${common.board_build.partitions}
|
||||||
lib_ldf_mode = ${common.lib_ldf_mode}
|
lib_ldf_mode = ${common.lib_ldf_mode}
|
||||||
upload_port = 192.168.1.38
|
upload_port = 192.168.1.38
|
||||||
upload_protocol = espota
|
upload_protocol = espota
|
||||||
@ -478,7 +590,7 @@ board_build.partitions = ${common.board_build.partitions} ; uncomment this to us
|
|||||||
lib_ldf_mode = ${common.lib_ldf_mode}
|
lib_ldf_mode = ${common.lib_ldf_mode}
|
||||||
upload_port = 192.168.1.38
|
upload_port = 192.168.1.38
|
||||||
upload_protocol = espota
|
upload_protocol = espota
|
||||||
upload_flags =
|
upload_flags =
|
||||||
--port=3232
|
--port=3232
|
||||||
--auth=12345678
|
--auth=12345678
|
||||||
build_type = release
|
build_type = release
|
||||||
@ -551,7 +663,7 @@ board_build.partitions = ${common.board_build.partitions} ; uncomment this to us
|
|||||||
lib_ldf_mode = ${common.lib_ldf_mode}
|
lib_ldf_mode = ${common.lib_ldf_mode}
|
||||||
upload_port = 192.168.1.38
|
upload_port = 192.168.1.38
|
||||||
upload_protocol = espota
|
upload_protocol = espota
|
||||||
upload_flags =
|
upload_flags =
|
||||||
--port=3232
|
--port=3232
|
||||||
--auth=12345678
|
--auth=12345678
|
||||||
build_type = release
|
build_type = release
|
||||||
@ -565,16 +677,13 @@ board_build.f_flash = 80000000L
|
|||||||
board_build.flash_mode = qio
|
board_build.flash_mode = qio
|
||||||
monitor_speed = ${common.monitor_speed}
|
monitor_speed = ${common.monitor_speed}
|
||||||
monitor_filters = ${common.monitor_filters}
|
monitor_filters = ${common.monitor_filters}
|
||||||
;build_unflags = ${common.build_unflags}
|
|
||||||
board_build.partitions = ${common.board_build.partitions}
|
board_build.partitions = ${common.board_build.partitions}
|
||||||
lib_ldf_mode = ${common.lib_ldf_mode}
|
lib_ldf_mode = ${common.lib_ldf_mode}
|
||||||
upload_speed = ${common.upload_speed}
|
upload_speed = ${common.upload_speed}
|
||||||
lib_deps = ${common.lib_deps}
|
lib_deps =
|
||||||
;upload_port = COM6
|
${common.lib_deps}
|
||||||
build_type = ${common.build_type}
|
build_type = ${common.build_type}
|
||||||
extra_scripts = ${common.extra_scripts}
|
extra_scripts = ${common.extra_scripts}
|
||||||
;monitor_rts = ${common.monitor_rts}
|
|
||||||
;monitor_dtr = ${common.monitor_dtr}
|
|
||||||
build_flags = ${common.build_flags}
|
build_flags = ${common.build_flags}
|
||||||
-DCORE_DEBUG_LEVEL=4 ; set the debug level
|
-DCORE_DEBUG_LEVEL=4 ; set the debug level
|
||||||
-DDEBUG_MODE=1 ; Set the debug mode
|
-DDEBUG_MODE=1 ; Set the debug mode
|
||||||
@ -588,23 +697,19 @@ board_build.f_flash = 80000000L
|
|||||||
board_build.flash_mode = qio
|
board_build.flash_mode = qio
|
||||||
monitor_speed = ${common.monitor_speed}
|
monitor_speed = ${common.monitor_speed}
|
||||||
monitor_filters = ${common.monitor_filters}
|
monitor_filters = ${common.monitor_filters}
|
||||||
;build_unflags = ${common.build_unflags}
|
board_build.partitions = ${common.board_build.partitions}
|
||||||
board_build.partitions = ${common.board_build.partitions} ; uncomment this to use the huge_app partition table, does not support OTA and only 1MB of SPIFFS
|
|
||||||
lib_ldf_mode = ${common.lib_ldf_mode}
|
lib_ldf_mode = ${common.lib_ldf_mode}
|
||||||
upload_speed = ${common.upload_speed}
|
upload_speed = ${common.upload_speed}
|
||||||
lib_deps = ${common.lib_deps}
|
lib_deps =
|
||||||
;upload_port = COM6
|
${common.lib_deps}
|
||||||
build_type = ${common.build_type}
|
build_type = ${common.build_type}
|
||||||
extra_scripts = ${common.extra_scripts}
|
extra_scripts = ${common.extra_scripts}
|
||||||
;monitor_rts = ${common.monitor_rts}
|
|
||||||
;monitor_dtr = ${common.monitor_dtr}
|
|
||||||
build_flags = ${common.build_flags}
|
build_flags = ${common.build_flags}
|
||||||
-DDEBUG_MODE=0 ; Set the debug mode
|
-DDEBUG_MODE=0 ; Set the debug mode
|
||||||
-DCORE_DEBUG_LEVEL=1
|
-DCORE_DEBUG_LEVEL=1
|
||||||
; CAMERA PINOUT DEFINITIONS
|
; CAMERA PINOUT DEFINITIONS
|
||||||
${pinoutsESPWROVER.build_flags}
|
${pinoutsESPWROVER.build_flags}
|
||||||
|
|
||||||
; Experimental OTA Environment - do not select unless you know what you are doing
|
|
||||||
[env:wrover_OTA]
|
[env:wrover_OTA]
|
||||||
platform = ${common.platform}
|
platform = ${common.platform}
|
||||||
board = esp-wrover-kit
|
board = esp-wrover-kit
|
||||||
@ -613,16 +718,13 @@ board_build.f_flash = 80000000L
|
|||||||
board_build.flash_mode = qio
|
board_build.flash_mode = qio
|
||||||
monitor_speed = ${common.monitor_speed}
|
monitor_speed = ${common.monitor_speed}
|
||||||
monitor_filters = ${common.monitor_filters}
|
monitor_filters = ${common.monitor_filters}
|
||||||
;build_unflags = ${common.build_unflags}
|
board_build.partitions = ${common.board_build.partitions}
|
||||||
board_build.partitions = ${common.board_build.partitions} ; uncomment this to use the min_spiffs partition table, great for using OTA
|
|
||||||
lib_ldf_mode = ${common.lib_ldf_mode}
|
lib_ldf_mode = ${common.lib_ldf_mode}
|
||||||
upload_speed = ${common.upload_speed}
|
upload_speed = ${common.upload_speed}
|
||||||
lib_deps = ${common.lib_deps}
|
lib_deps =
|
||||||
|
${common.lib_deps}
|
||||||
build_type = release
|
build_type = release
|
||||||
extra_scripts = ${common.extra_scripts}
|
extra_scripts = ${common.extra_scripts}
|
||||||
;monitor_rts = ${common.monitor_rts}
|
|
||||||
;monitor_dtr = ${common.monitor_dtr}
|
|
||||||
|
|
||||||
build_flags = ${common.build_flags}
|
build_flags = ${common.build_flags}
|
||||||
-DDEBUG_MODE=0 ; Set the debug mode
|
-DDEBUG_MODE=0 ; Set the debug mode
|
||||||
-DCORE_DEBUG_LEVEL=1
|
-DCORE_DEBUG_LEVEL=1
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <network/WifiHandler/WifiHandler.hpp>
|
#include <network/WifiHandler/WifiHandler.hpp>
|
||||||
#include <network/mDNS/MDNSManager.hpp>
|
#include <network/mDNS/MDNSManager.hpp>
|
||||||
|
#include <network/mDNS/auto/AutoDiscovery.hpp>
|
||||||
#include <io/camera/cameraHandler.hpp>
|
#include <io/camera/cameraHandler.hpp>
|
||||||
#include <io/LEDManager/LEDManager.hpp>
|
#include <io/LEDManager/LEDManager.hpp>
|
||||||
#include <network/stream/streamServer.hpp>
|
#include <network/stream/streamServer.hpp>
|
||||||
@ -9,12 +10,12 @@
|
|||||||
//! TODO: Setup OTA enabled state to be controllable by API if enabled at compile time
|
//! TODO: Setup OTA enabled state to be controllable by API if enabled at compile time
|
||||||
#if ENABLE_OTA
|
#if ENABLE_OTA
|
||||||
#include <network/OTA/OTA.hpp>
|
#include <network/OTA/OTA.hpp>
|
||||||
|
#include "network/OTA/Github_OTA/GithubOTAHandler.hpp"
|
||||||
#endif // ENABLE_OTA
|
#endif // ENABLE_OTA
|
||||||
#include <logo/logo.hpp>
|
#include <logo/logo.hpp>
|
||||||
#include <data/config/project_config.hpp>
|
#include <data/config/project_config.hpp>
|
||||||
|
|
||||||
//#include <data/utilities/makeunique.hpp>
|
// #include <data/utilities/makeunique.hpp>
|
||||||
//#include <io/SerialManager/serialmanager.hpp> // Serial Manager
|
|
||||||
|
|
||||||
int STREAM_SERVER_PORT = 80;
|
int STREAM_SERVER_PORT = 80;
|
||||||
int CONTROL_SERVER_PORT = 81;
|
int CONTROL_SERVER_PORT = 81;
|
||||||
@ -28,13 +29,15 @@ int CONTROL_SERVER_PORT = 81;
|
|||||||
ProjectConfig deviceConfig("openiris", MDNS_HOSTNAME);
|
ProjectConfig deviceConfig("openiris", MDNS_HOSTNAME);
|
||||||
#if ENABLE_OTA
|
#if ENABLE_OTA
|
||||||
OTA ota(&deviceConfig);
|
OTA ota(&deviceConfig);
|
||||||
|
GithubOTAHandler otaHandler;
|
||||||
#endif // ENABLE_OTA
|
#endif // ENABLE_OTA
|
||||||
LEDManager ledManager(33);
|
LEDManager ledManager(33);
|
||||||
CameraHandler cameraHandler(&deviceConfig, &ledStateManager);
|
CameraHandler cameraHandler(&deviceConfig, &ledStateManager);
|
||||||
// SerialManager serialManager(&deviceConfig);
|
// SerialManager serialManager(&deviceConfig);
|
||||||
WiFiHandler wifiHandler(&deviceConfig, &wifiStateManager, WIFI_SSID, WIFI_PASSWORD, WIFI_CHANNEL);
|
WiFiHandler wifiHandler(&deviceConfig, &wifiStateManager, WIFI_SSID, WIFI_PASSWORD, WIFI_CHANNEL);
|
||||||
APIServer apiServer(CONTROL_SERVER_PORT, &deviceConfig, &cameraHandler, &wifiStateManager, "/control");
|
APIServer apiServer(CONTROL_SERVER_PORT, &deviceConfig, &cameraHandler, &wifiStateManager, &otaHandler, "/control");
|
||||||
MDNSHandler mdnsHandler(&mdnsStateManager, &deviceConfig);
|
MDNSHandler mdnsHandler(&mdnsStateManager, &deviceConfig);
|
||||||
|
AutoDiscovery autoDiscoveryHandler(&mdnsStateManager, &deviceConfig);
|
||||||
StreamServer streamServer(STREAM_SERVER_PORT);
|
StreamServer streamServer(STREAM_SERVER_PORT);
|
||||||
|
|
||||||
void setup()
|
void setup()
|
||||||
@ -51,7 +54,22 @@ void setup()
|
|||||||
deviceConfig.load();
|
deviceConfig.load();
|
||||||
wifiHandler._enable_adhoc = ENABLE_ADHOC;
|
wifiHandler._enable_adhoc = ENABLE_ADHOC;
|
||||||
wifiHandler.setupWifi();
|
wifiHandler.setupWifi();
|
||||||
mdnsHandler.startMDNS();
|
|
||||||
|
mdnsStateManager.setState(MDNSState_e::MDNSState_Starting);
|
||||||
|
switch (mdnsStateManager.getCurrentState())
|
||||||
|
{
|
||||||
|
case MDNSState_e::MDNSState_Starting:
|
||||||
|
autoDiscoveryHandler.start();
|
||||||
|
break;
|
||||||
|
case MDNSState_e::MDNSState_Error:
|
||||||
|
break;
|
||||||
|
case MDNSState_e::MDNSState_QueryComplete:
|
||||||
|
mdnsHandler.startMDNS();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
switch (wifiStateManager.getCurrentState())
|
switch (wifiStateManager.getCurrentState())
|
||||||
{
|
{
|
||||||
case WiFiState_e::WiFiState_Disconnected:
|
case WiFiState_e::WiFiState_Disconnected:
|
||||||
|
|||||||
1
ESP/tools/version.txt
Normal file
1
ESP/tools/version.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
v1.0.10
|
||||||
Loading…
Reference in New Issue
Block a user