Compare commits
2 Commits
taskDriven
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 56d8a0f2cf | |||
| bec3d91d98 |
1
components/esp-nimble-cpp
Submodule
1
components/esp-nimble-cpp
Submodule
Submodule components/esp-nimble-cpp added at 25af28bcad
@@ -13,28 +13,8 @@ dependencies:
|
||||
registry_url: https://components.espressif.com/
|
||||
type: service
|
||||
version: 1.0.0
|
||||
esp-nimble-cpp:
|
||||
component_hash: 224980602c685130c426248ad9a0500686d4a5aff0ec3a10b6dfacf00c554a91
|
||||
dependencies:
|
||||
- name: espressif/esp_hosted
|
||||
rules:
|
||||
- if: target in [esp32p4]
|
||||
version: '*'
|
||||
- name: espressif/esp_wifi_remote
|
||||
rules:
|
||||
- if: target in [esp32p4]
|
||||
version: '>=0.5.3'
|
||||
- name: idf
|
||||
rules:
|
||||
- if: target in [esp32p4]
|
||||
version: '>=5.3.0'
|
||||
source:
|
||||
git: https://github.com/h2zero/esp-nimble-cpp.git
|
||||
path: .
|
||||
type: git
|
||||
version: 002abf91e9779ea5646d75278ae52c6b848d3fa0
|
||||
espressif/esp_websocket_client:
|
||||
component_hash: c5a067a9fddea370c478017e66fac302f4b79c3d4027e9bdd42a019786cceb92
|
||||
component_hash: 723aba370113196c66321442426cd6452c351eef31c85c83bd1446831ef9f8f4
|
||||
dependencies:
|
||||
- name: idf
|
||||
require: private
|
||||
@@ -42,14 +22,13 @@ dependencies:
|
||||
source:
|
||||
registry_url: https://components.espressif.com
|
||||
type: service
|
||||
version: 1.6.1
|
||||
version: 1.6.0
|
||||
idf:
|
||||
source:
|
||||
type: idf
|
||||
version: 5.5.1
|
||||
direct_dependencies:
|
||||
- bubblesnake/esp_socketio_client
|
||||
- esp-nimble-cpp
|
||||
manifest_hash: 5f4bfc48b0eb389591b06fcf0a5c43c05e081427d594466829c6f7c545158221
|
||||
manifest_hash: d73c96c5d6ddd24707089a2953e50f36a12ebbc66b5458ada3d4f55c0987ccf1
|
||||
target: esp32c6
|
||||
version: 2.0.0
|
||||
|
||||
@@ -23,17 +23,6 @@ class MyCharCallbacks : public NimBLECharacteristicCallbacks {
|
||||
};
|
||||
|
||||
NimBLEAdvertising* initBLE();
|
||||
|
||||
void BLE_manager_task(void *pvParameters);
|
||||
|
||||
// Event Types
|
||||
typedef enum {
|
||||
EVENT_SCAN_REQUESTED,
|
||||
EVENT_TOKEN_GIVEN,
|
||||
EVENT_CREDS_GIVEN,
|
||||
EVENT_SHUTDOWN
|
||||
} BLE_event_type_t;
|
||||
|
||||
bool tokenCheck();
|
||||
bool BLEtick(NimBLEAdvertising* pAdvertising);
|
||||
|
||||
#endif
|
||||
@@ -14,10 +14,9 @@ class WiFi {
|
||||
const std::string password, const wifi_auth_mode_t authMode);
|
||||
static bool isConnected();
|
||||
static void scanAndUpdateSSIDList();
|
||||
static bool attemptDHCPrenewal();
|
||||
private:
|
||||
static TaskHandle_t awaitConnectHandle;
|
||||
static void processScanResults();
|
||||
static std::atomic<bool> authFailed;
|
||||
static bool awaitConnected();
|
||||
static esp_event_handler_instance_t instance_any_id;
|
||||
static esp_event_handler_instance_t instance_got_ip;
|
||||
@@ -28,4 +27,6 @@ class WiFi {
|
||||
static std::string getIP();
|
||||
};
|
||||
|
||||
extern WiFi bmWiFi;
|
||||
|
||||
#endif
|
||||
@@ -6,7 +6,6 @@
|
||||
extern std::string webToken;
|
||||
|
||||
bool httpGET(std::string endpoint, std::string token, cJSON* &JSONresponse);
|
||||
bool httpPOST(std::string endpoint, std::string token, cJSON* postData, cJSON* &JSONresponse);
|
||||
|
||||
void deleteWiFiAndTokenDetails();
|
||||
|
||||
|
||||
@@ -1,27 +1,24 @@
|
||||
#ifndef CALIBRATION_H
|
||||
#define CALIBRATION_H
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include <atomic>
|
||||
#include "encoder.hpp"
|
||||
|
||||
class Calibration {
|
||||
public:
|
||||
static void init();
|
||||
static bool beginDownwardCalib(Encoder& topEnc);
|
||||
static bool completeCalib(Encoder& topEnc);
|
||||
static int32_t convertToTicks(uint8_t appPos);
|
||||
static uint8_t convertToAppPos(int32_t ticks);
|
||||
static bool getCalibrated() {return calibrated;}
|
||||
static bool clearCalibrated();
|
||||
static std::atomic<int32_t> DownTicks;
|
||||
static std::atomic<int32_t> UpTicks;
|
||||
void init();
|
||||
bool beginDownwardCalib(Encoder& topEnc);
|
||||
bool completeCalib(Encoder& topEnc);
|
||||
int32_t convertToTicks(uint8_t appPos);
|
||||
uint8_t convertToAppPos(int32_t ticks);
|
||||
bool getCalibrated() {return calibrated;}
|
||||
bool clearCalibrated();
|
||||
std::atomic<int32_t> DownTicks;
|
||||
std::atomic<int32_t> UpTicks;
|
||||
|
||||
private:
|
||||
static std::atomic<bool> calibrated;
|
||||
std::atomic<bool> calibrated;
|
||||
};
|
||||
|
||||
extern TaskHandle_t calibTaskHandle;
|
||||
bool calibrate();
|
||||
extern Calibration calib;
|
||||
|
||||
#endif
|
||||
@@ -31,16 +31,16 @@
|
||||
// #define srvAddr "192.168.1.190:3000"
|
||||
#define srvAddr "wahwa.com"
|
||||
|
||||
#define ENCODER_PIN_A GPIO_NUM_21 // d3
|
||||
#define ENCODER_PIN_A GPIO_NUM_23 // d5
|
||||
#define ENCODER_PIN_B GPIO_NUM_16 // d6
|
||||
|
||||
#define InputEnc_PIN_A GPIO_NUM_0 // d0
|
||||
#define InputEnc_PIN_B GPIO_NUM_1 // d1
|
||||
#define InputEnc_PIN_A GPIO_NUM_1 // d1
|
||||
#define InputEnc_PIN_B GPIO_NUM_2 // d2
|
||||
|
||||
#define servoPin GPIO_NUM_20
|
||||
#define servoLEDCChannel LEDC_CHANNEL_0
|
||||
#define servoSwitch GPIO_NUM_17
|
||||
|
||||
#define debugLED GPIO_NUM_18 // d10
|
||||
#define debugLED GPIO_NUM_22 // d4
|
||||
|
||||
#endif
|
||||
@@ -1,22 +0,0 @@
|
||||
#ifndef I2C_HELPER_H
|
||||
#define I2C_HELPER_H
|
||||
#include "driver/gpio.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// I2C Configuration (Match your schematic)
|
||||
#define I2C_MASTER_SCL_IO GPIO_NUM_23 // Example GPIO for C6
|
||||
#define I2C_MASTER_SDA_IO GPIO_NUM_22 // Example GPIO for C6
|
||||
#define I2C_MASTER_NUM 0
|
||||
#define I2C_MASTER_FREQ_HZ 100000 // use standard freq
|
||||
#define I2C_MASTER_TIMEOUT_MS 1000
|
||||
|
||||
esp_err_t i2c_init();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,34 +0,0 @@
|
||||
#ifndef BM_EVENTS_H
|
||||
#define BM_EVENTS_H
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Shared with max17048.c (C) — only C-compatible types here
|
||||
typedef enum {
|
||||
EVENT_CLEAR_CALIB,
|
||||
EVENT_SAVE_POS,
|
||||
EVENT_REQUEST_POS,
|
||||
EVENT_BATTERY_CRITICAL, // stop servo, save pos, alert server, deep sleep
|
||||
EVENT_BATTERY_WARNING, // alert server (no shutdown)
|
||||
EVENT_REPORT_SOC, // periodic SOC sync to server
|
||||
} main_event_type_t;
|
||||
|
||||
extern QueueHandle_t main_event_queue;
|
||||
extern TaskHandle_t wakeTaskHandle;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
void mainEventLoop();
|
||||
void wakeTimer(void* pvParameters);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,73 +0,0 @@
|
||||
#ifndef MAX_17_H
|
||||
#define MAX_17_H
|
||||
|
||||
#define MAX17048_ADDR 0x36
|
||||
#define MAX17048_REG_VCELL 0x02
|
||||
#define MAX17048_REG_SOC 0x04
|
||||
#define MAX17048_REG_MODE 0x06
|
||||
#define MAX17048_REG_VERSION 0x08
|
||||
#define MAX17048_REG_CONFIG 0x0C
|
||||
#define MAX17048_REG_VALRT 0x14
|
||||
#define MAX17048_REG_VRST_ID 0x18
|
||||
#define MAX17048_REG_STATUS 0x1A
|
||||
#define MAX17048_REG_CMD 0xFE
|
||||
|
||||
#define MAX17048_CMD_POR 0x5400
|
||||
#define MAX17048_CMD_QSTRT 0x4000
|
||||
|
||||
// NOTE: maxALRT (GPIO_NUM_2) conflicts with InputEnc_PIN_B in defines.h.
|
||||
// Assign maxALRT to a free GPIO before enabling the interrupt.
|
||||
#define maxALRT GPIO_NUM_2
|
||||
|
||||
// STATUS register MSB (addr 0x1A) bit masks
|
||||
// [7:X] [6:EnVR] [5:SC] [4:HD] [3:VR] [2:VL] [1:VH] [0:RI]
|
||||
#define SCbit (1 << 5) // SOC changed by 1%
|
||||
#define HDbit (1 << 4) // SOC crossed low threshold (CONFIG.ATHD)
|
||||
#define VRbit (1 << 3) // voltage reset alert
|
||||
#define VLbit (1 << 2) // VCELL below VALRT.MIN
|
||||
#define VHbit (1 << 1) // VCELL above VALRT.MAX
|
||||
#define RIbit (1 << 0) // reset indicator (device just powered on)
|
||||
|
||||
// SOC thresholds for user-facing push notifications / shutdown logic
|
||||
#define SOC_WARN_20 20 // emit warning push at this level
|
||||
#define SOC_WARN_10 10 // emit critical push at this level
|
||||
#define SOC_CRITICAL_VL 5 // treat undervoltage as critical only below this SOC
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "driver/i2c.h"
|
||||
|
||||
// Alert type carried from bms_checker_task to mainEventLoop via bms_pending_alert
|
||||
typedef enum {
|
||||
BATT_ALERT_OVERVOLTAGE, // VH: charging fault / damaged battery
|
||||
BATT_ALERT_CRITICAL_LOW, // HD, or VL + SOC < SOC_CRITICAL_VL
|
||||
BATT_ALERT_LOW_VOLTAGE_WARNING, // VL with SOC >= SOC_CRITICAL_VL (transient dip)
|
||||
BATT_ALERT_SOC_LOW_20, // SOC just crossed 20% downward
|
||||
BATT_ALERT_SOC_LOW_10, // SOC just crossed 10% downward
|
||||
} batt_alert_type_t;
|
||||
|
||||
extern uint8_t established_soc;
|
||||
extern volatile batt_alert_type_t bms_pending_alert;
|
||||
|
||||
esp_err_t max17048_init();
|
||||
uint8_t bms_get_soc();
|
||||
esp_err_t bms_set_alert_bound_voltages(float min, float max);
|
||||
esp_err_t bms_set_reset_voltage(float vreset);
|
||||
void bms_checker_task(void *pvParameters);
|
||||
|
||||
#define bms_set_alsc() max17048_friendly_write_reg(MAX17048_REG_CONFIG, 0, 1<<6, 0, 1<<6)
|
||||
#define bms_clear_status() max17048_write_reg(MAX17048_REG_STATUS, 0, 0)
|
||||
#define bms_clear_alrt() max17048_friendly_write_reg(MAX17048_REG_CONFIG, 0, 0, 0, 1<<5)
|
||||
|
||||
esp_err_t max17048_read_reg(uint8_t reg_addr, uint8_t *MSB, uint8_t *LSB);
|
||||
esp_err_t max17048_write_reg(uint8_t reg_addr, uint8_t MSB, uint8_t LSB);
|
||||
esp_err_t max17048_friendly_write_reg(uint8_t reg_addr, uint8_t MSB, uint8_t LSB,
|
||||
uint8_t MSBmask, uint8_t LSBmask);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -10,6 +10,8 @@
|
||||
#define manual 0
|
||||
|
||||
extern std::atomic<bool> calibListen;
|
||||
extern std::atomic<bool> clearCalibFlag;
|
||||
extern std::atomic<bool> savePosFlag;
|
||||
|
||||
extern Encoder* topEnc;
|
||||
extern Encoder* bottomEnc;
|
||||
|
||||
@@ -1,16 +1,7 @@
|
||||
#ifndef SETUP_H
|
||||
#define SETUP_H
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include <atomic>
|
||||
|
||||
extern TaskHandle_t setupTaskHandle;
|
||||
extern std::atomic<bool> awaitCalibration;
|
||||
|
||||
void initialSetup();
|
||||
void setupLoop();
|
||||
|
||||
void setupAndCalibrate();
|
||||
|
||||
#endif
|
||||
@@ -2,7 +2,8 @@
|
||||
#define SOCKETIO_HPP
|
||||
#include <atomic>
|
||||
|
||||
extern std::atomic<bool> socketIOactive;
|
||||
extern std::atomic<bool> statusResolved;
|
||||
extern std::atomic<bool> connected;
|
||||
|
||||
// Initialize Socket.IO client and connect to server
|
||||
void initSocketIO();
|
||||
|
||||
@@ -1152,6 +1152,7 @@ CONFIG_ESP_TLS_DYN_BUF_STRATEGY_SUPPORTED=y
|
||||
CONFIG_ESP_COEX_ENABLED=y
|
||||
CONFIG_ESP_COEX_SW_COEXIST_ENABLE=y
|
||||
# CONFIG_ESP_COEX_POWER_MANAGEMENT is not set
|
||||
# CONFIG_ESP_COEX_GPIO_DEBUG is not set
|
||||
# end of Wireless Coexistence
|
||||
|
||||
#
|
||||
@@ -1292,7 +1293,6 @@ CONFIG_SPI_SLAVE_ISR_IN_IRAM=y
|
||||
# ESP-Driver:USB Serial/JTAG Configuration
|
||||
#
|
||||
CONFIG_USJ_ENABLE_USB_SERIAL_JTAG=y
|
||||
# CONFIG_USJ_NO_AUTO_LS_ON_CONNECTION is not set
|
||||
# end of ESP-Driver:USB Serial/JTAG Configuration
|
||||
|
||||
#
|
||||
@@ -1423,7 +1423,6 @@ CONFIG_ESP_SLEEP_WAIT_FLASH_READY_EXTRA_DELAY=0
|
||||
# CONFIG_ESP_SLEEP_CACHE_SAFE_ASSERTION is not set
|
||||
# CONFIG_ESP_SLEEP_DEBUG is not set
|
||||
CONFIG_ESP_SLEEP_GPIO_ENABLE_INTERNAL_RESISTORS=y
|
||||
# CONFIG_ESP_SLEEP_EVENT_CALLBACKS is not set
|
||||
# end of Sleep Config
|
||||
|
||||
#
|
||||
@@ -1528,7 +1527,6 @@ CONFIG_ESP_PHY_CALIBRATION_AND_DATA_STORAGE=y
|
||||
# CONFIG_ESP_PHY_INIT_DATA_IN_PARTITION is not set
|
||||
CONFIG_ESP_PHY_MAX_WIFI_TX_POWER=20
|
||||
CONFIG_ESP_PHY_MAX_TX_POWER=20
|
||||
CONFIG_ESP_PHY_MAC_BB_PD=y
|
||||
# CONFIG_ESP_PHY_REDUCE_TX_POWER is not set
|
||||
# CONFIG_ESP_PHY_ENABLE_CERT_TEST is not set
|
||||
CONFIG_ESP_PHY_RF_CAL_PARTIAL=y
|
||||
@@ -1546,18 +1544,11 @@ CONFIG_ESP_PHY_IRAM_OPT=y
|
||||
# Power Management
|
||||
#
|
||||
CONFIG_PM_SLEEP_FUNC_IN_IRAM=y
|
||||
CONFIG_PM_ENABLE=y
|
||||
CONFIG_PM_DFS_INIT_AUTO=y
|
||||
# CONFIG_PM_PROFILING is not set
|
||||
CONFIG_PM_TRACE=y
|
||||
# CONFIG_PM_ENABLE is not set
|
||||
CONFIG_PM_SLP_IRAM_OPT=y
|
||||
CONFIG_PM_RTOS_IDLE_OPT=y
|
||||
CONFIG_PM_SLP_DISABLE_GPIO=y
|
||||
CONFIG_PM_SLP_DEFAULT_PARAMS_OPT=y
|
||||
CONFIG_PM_LIGHTSLEEP_RTC_OSC_CAL_INTERVAL=1
|
||||
CONFIG_PM_POWER_DOWN_CPU_IN_LIGHT_SLEEP=y
|
||||
CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP=y
|
||||
# CONFIG_PM_LIGHT_SLEEP_CALLBACKS is not set
|
||||
# CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP is not set
|
||||
# end of Power Management
|
||||
|
||||
#
|
||||
@@ -1620,7 +1611,7 @@ CONFIG_ESP_SYSTEM_PMP_IDRAM_SPLIT=y
|
||||
|
||||
CONFIG_ESP_SYSTEM_EVENT_QUEUE_SIZE=32
|
||||
CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=2304
|
||||
CONFIG_ESP_MAIN_TASK_STACK_SIZE=8192
|
||||
CONFIG_ESP_MAIN_TASK_STACK_SIZE=3584
|
||||
CONFIG_ESP_MAIN_TASK_AFFINITY_CPU0=y
|
||||
# CONFIG_ESP_MAIN_TASK_AFFINITY_NO_AFFINITY is not set
|
||||
CONFIG_ESP_MAIN_TASK_AFFINITY=0x0
|
||||
@@ -1716,7 +1707,6 @@ CONFIG_ESP_WIFI_STA_DISCONNECTED_PM_ENABLE=y
|
||||
# CONFIG_ESP_WIFI_GCMP_SUPPORT is not set
|
||||
CONFIG_ESP_WIFI_GMAC_SUPPORT=y
|
||||
CONFIG_ESP_WIFI_SOFTAP_SUPPORT=y
|
||||
# CONFIG_ESP_WIFI_ENHANCED_LIGHT_SLEEP is not set
|
||||
# CONFIG_ESP_WIFI_SLP_BEACON_LOST_OPT is not set
|
||||
CONFIG_ESP_WIFI_ESPNOW_MAX_ENCRYPT_NUM=7
|
||||
CONFIG_ESP_WIFI_MBEDTLS_CRYPTO=y
|
||||
@@ -1839,8 +1829,6 @@ CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=1
|
||||
# CONFIG_FREERTOS_USE_TRACE_FACILITY is not set
|
||||
# CONFIG_FREERTOS_USE_LIST_DATA_INTEGRITY_CHECK_BYTES is not set
|
||||
# CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS is not set
|
||||
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
|
||||
CONFIG_FREERTOS_IDLE_TIME_BEFORE_SLEEP=3
|
||||
# CONFIG_FREERTOS_USE_APPLICATION_TASK_TAG is not set
|
||||
# end of Kernel
|
||||
|
||||
@@ -1920,7 +1908,6 @@ CONFIG_IEEE802154_CCA_THRESHOLD=-60
|
||||
CONFIG_IEEE802154_PENDING_TABLE_SIZE=20
|
||||
# CONFIG_IEEE802154_MULTI_PAN_ENABLE is not set
|
||||
CONFIG_IEEE802154_TIMING_OPTIMIZATION=y
|
||||
# CONFIG_IEEE802154_SLEEP_ENABLE is not set
|
||||
# CONFIG_IEEE802154_DEBUG is not set
|
||||
# CONFIG_IEEE802154_DEBUG_ASSERT_MONITOR is not set
|
||||
# end of IEEE 802.15.4
|
||||
@@ -2568,10 +2555,6 @@ CONFIG_WIFI_PROV_STA_ALL_CHANNEL_SCAN=y
|
||||
# CONFIG_WIFI_PROV_STA_FAST_SCAN is not set
|
||||
# end of Wi-Fi Provisioning Manager
|
||||
|
||||
#
|
||||
# ESP Socket.IO client
|
||||
#
|
||||
|
||||
#
|
||||
# ESP-NimBLE-CPP configuration
|
||||
#
|
||||
@@ -2594,6 +2577,10 @@ CONFIG_NIMBLE_CPP_FREERTOS_TASK_BLOCK_BIT=31
|
||||
CONFIG_NIMBLE_CPP_IDF=y
|
||||
# end of ESP-NimBLE-CPP configuration
|
||||
|
||||
#
|
||||
# ESP Socket.IO client
|
||||
#
|
||||
|
||||
#
|
||||
# ESP WebSocket client
|
||||
#
|
||||
@@ -2701,14 +2688,12 @@ CONFIG_ESP32_PHY_CALIBRATION_AND_DATA_STORAGE=y
|
||||
# CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION is not set
|
||||
CONFIG_ESP32_PHY_MAX_WIFI_TX_POWER=20
|
||||
CONFIG_ESP32_PHY_MAX_TX_POWER=20
|
||||
CONFIG_MAC_BB_PD=y
|
||||
CONFIG_ESP32_PHY_MAC_BB_PD=y
|
||||
# CONFIG_REDUCE_PHY_TX_POWER is not set
|
||||
# CONFIG_ESP32_REDUCE_PHY_TX_POWER is not set
|
||||
CONFIG_ESP_SYSTEM_PM_POWER_DOWN_CPU=y
|
||||
CONFIG_SYSTEM_EVENT_QUEUE_SIZE=32
|
||||
CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE=2304
|
||||
CONFIG_MAIN_TASK_STACK_SIZE=8192
|
||||
CONFIG_MAIN_TASK_STACK_SIZE=3584
|
||||
CONFIG_CONSOLE_UART_DEFAULT=y
|
||||
# CONFIG_CONSOLE_UART_CUSTOM is not set
|
||||
# CONFIG_CONSOLE_UART_NONE is not set
|
||||
@@ -2807,4 +2792,5 @@ CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS=y
|
||||
CONFIG_SUPPRESS_SELECT_DEBUG_OUTPUT=y
|
||||
CONFIG_SUPPORT_TERMIOS=y
|
||||
CONFIG_SEMIHOSTFS_MAX_MOUNT_POINTS=1
|
||||
CONFIG_NIMBLE_ENABLED=y
|
||||
# End of deprecated options
|
||||
|
||||
@@ -132,6 +132,7 @@ CONFIG_SOC_GPIO_DEEP_SLEEP_WAKE_VALID_GPIO_MASK=0
|
||||
CONFIG_SOC_GPIO_DEEP_SLEEP_WAKE_SUPPORTED_PIN_CNT=8
|
||||
CONFIG_SOC_GPIO_VALID_DIGITAL_IO_PAD_MASK=0x000000007FFFFF00
|
||||
CONFIG_SOC_GPIO_SUPPORT_FORCE_HOLD=y
|
||||
CONFIG_SOC_GPIO_SUPPORT_HOLD_IO_IN_DSLP=y
|
||||
CONFIG_SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP=y
|
||||
CONFIG_SOC_GPIO_CLOCKOUT_BY_GPIO_MATRIX=y
|
||||
CONFIG_SOC_CLOCKOUT_HAS_SOURCE_GATE=y
|
||||
@@ -1152,6 +1153,7 @@ CONFIG_ESP_TLS_DYN_BUF_STRATEGY_SUPPORTED=y
|
||||
CONFIG_ESP_COEX_ENABLED=y
|
||||
CONFIG_ESP_COEX_SW_COEXIST_ENABLE=y
|
||||
# CONFIG_ESP_COEX_POWER_MANAGEMENT is not set
|
||||
# CONFIG_ESP_COEX_GPIO_DEBUG is not set
|
||||
# end of Wireless Coexistence
|
||||
|
||||
#
|
||||
@@ -1292,7 +1294,6 @@ CONFIG_SPI_SLAVE_ISR_IN_IRAM=y
|
||||
# ESP-Driver:USB Serial/JTAG Configuration
|
||||
#
|
||||
CONFIG_USJ_ENABLE_USB_SERIAL_JTAG=y
|
||||
# CONFIG_USJ_NO_AUTO_LS_ON_CONNECTION is not set
|
||||
# end of ESP-Driver:USB Serial/JTAG Configuration
|
||||
|
||||
#
|
||||
@@ -1423,7 +1424,6 @@ CONFIG_ESP_SLEEP_WAIT_FLASH_READY_EXTRA_DELAY=0
|
||||
# CONFIG_ESP_SLEEP_CACHE_SAFE_ASSERTION is not set
|
||||
# CONFIG_ESP_SLEEP_DEBUG is not set
|
||||
CONFIG_ESP_SLEEP_GPIO_ENABLE_INTERNAL_RESISTORS=y
|
||||
# CONFIG_ESP_SLEEP_EVENT_CALLBACKS is not set
|
||||
# end of Sleep Config
|
||||
|
||||
#
|
||||
@@ -1528,7 +1528,6 @@ CONFIG_ESP_PHY_CALIBRATION_AND_DATA_STORAGE=y
|
||||
# CONFIG_ESP_PHY_INIT_DATA_IN_PARTITION is not set
|
||||
CONFIG_ESP_PHY_MAX_WIFI_TX_POWER=20
|
||||
CONFIG_ESP_PHY_MAX_TX_POWER=20
|
||||
CONFIG_ESP_PHY_MAC_BB_PD=y
|
||||
# CONFIG_ESP_PHY_REDUCE_TX_POWER is not set
|
||||
# CONFIG_ESP_PHY_ENABLE_CERT_TEST is not set
|
||||
CONFIG_ESP_PHY_RF_CAL_PARTIAL=y
|
||||
@@ -1546,18 +1545,11 @@ CONFIG_ESP_PHY_IRAM_OPT=y
|
||||
# Power Management
|
||||
#
|
||||
CONFIG_PM_SLEEP_FUNC_IN_IRAM=y
|
||||
CONFIG_PM_ENABLE=y
|
||||
CONFIG_PM_DFS_INIT_AUTO=y
|
||||
# CONFIG_PM_PROFILING is not set
|
||||
CONFIG_PM_TRACE=y
|
||||
# CONFIG_PM_ENABLE is not set
|
||||
CONFIG_PM_SLP_IRAM_OPT=y
|
||||
CONFIG_PM_RTOS_IDLE_OPT=y
|
||||
CONFIG_PM_SLP_DISABLE_GPIO=y
|
||||
CONFIG_PM_SLP_DEFAULT_PARAMS_OPT=y
|
||||
CONFIG_PM_LIGHTSLEEP_RTC_OSC_CAL_INTERVAL=1
|
||||
CONFIG_PM_POWER_DOWN_CPU_IN_LIGHT_SLEEP=y
|
||||
CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP=y
|
||||
# CONFIG_PM_LIGHT_SLEEP_CALLBACKS is not set
|
||||
# CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP is not set
|
||||
# end of Power Management
|
||||
|
||||
#
|
||||
@@ -1672,7 +1664,7 @@ CONFIG_ESP_TIMER_INTERRUPT_LEVEL=1
|
||||
CONFIG_ESP_TIMER_TASK_AFFINITY=0x0
|
||||
CONFIG_ESP_TIMER_TASK_AFFINITY_CPU0=y
|
||||
CONFIG_ESP_TIMER_ISR_AFFINITY_CPU0=y
|
||||
CONFIG_ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD=y
|
||||
# CONFIG_ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD is not set
|
||||
CONFIG_ESP_TIMER_IMPL_SYSTIMER=y
|
||||
# end of ESP Timer (High Resolution Timer)
|
||||
|
||||
@@ -1716,7 +1708,6 @@ CONFIG_ESP_WIFI_STA_DISCONNECTED_PM_ENABLE=y
|
||||
# CONFIG_ESP_WIFI_GCMP_SUPPORT is not set
|
||||
CONFIG_ESP_WIFI_GMAC_SUPPORT=y
|
||||
CONFIG_ESP_WIFI_SOFTAP_SUPPORT=y
|
||||
# CONFIG_ESP_WIFI_ENHANCED_LIGHT_SLEEP is not set
|
||||
# CONFIG_ESP_WIFI_SLP_BEACON_LOST_OPT is not set
|
||||
CONFIG_ESP_WIFI_ESPNOW_MAX_ENCRYPT_NUM=7
|
||||
CONFIG_ESP_WIFI_MBEDTLS_CRYPTO=y
|
||||
@@ -1839,8 +1830,6 @@ CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=1
|
||||
# CONFIG_FREERTOS_USE_TRACE_FACILITY is not set
|
||||
# CONFIG_FREERTOS_USE_LIST_DATA_INTEGRITY_CHECK_BYTES is not set
|
||||
# CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS is not set
|
||||
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
|
||||
CONFIG_FREERTOS_IDLE_TIME_BEFORE_SLEEP=3
|
||||
# CONFIG_FREERTOS_USE_APPLICATION_TASK_TAG is not set
|
||||
# end of Kernel
|
||||
|
||||
@@ -1920,7 +1909,6 @@ CONFIG_IEEE802154_CCA_THRESHOLD=-60
|
||||
CONFIG_IEEE802154_PENDING_TABLE_SIZE=20
|
||||
# CONFIG_IEEE802154_MULTI_PAN_ENABLE is not set
|
||||
CONFIG_IEEE802154_TIMING_OPTIMIZATION=y
|
||||
# CONFIG_IEEE802154_SLEEP_ENABLE is not set
|
||||
# CONFIG_IEEE802154_DEBUG is not set
|
||||
# CONFIG_IEEE802154_DEBUG_ASSERT_MONITOR is not set
|
||||
# end of IEEE 802.15.4
|
||||
@@ -2356,6 +2344,13 @@ CONFIG_LIBC_TIME_SYSCALL_USE_RTC_HRT=y
|
||||
#
|
||||
# CONFIG_OPENTHREAD_ENABLED is not set
|
||||
|
||||
#
|
||||
# Thread Console
|
||||
#
|
||||
CONFIG_OPENTHREAD_CLI=y
|
||||
CONFIG_OPENTHREAD_CONSOLE_COMMAND_PREFIX="ot"
|
||||
# end of Thread Console
|
||||
|
||||
#
|
||||
# OpenThread Spinel
|
||||
#
|
||||
@@ -2568,10 +2563,6 @@ CONFIG_WIFI_PROV_STA_ALL_CHANNEL_SCAN=y
|
||||
# CONFIG_WIFI_PROV_STA_FAST_SCAN is not set
|
||||
# end of Wi-Fi Provisioning Manager
|
||||
|
||||
#
|
||||
# ESP Socket.IO client
|
||||
#
|
||||
|
||||
#
|
||||
# ESP-NimBLE-CPP configuration
|
||||
#
|
||||
@@ -2594,6 +2585,10 @@ CONFIG_NIMBLE_CPP_FREERTOS_TASK_BLOCK_BIT=31
|
||||
CONFIG_NIMBLE_CPP_IDF=y
|
||||
# end of ESP-NimBLE-CPP configuration
|
||||
|
||||
#
|
||||
# ESP Socket.IO client
|
||||
#
|
||||
|
||||
#
|
||||
# ESP WebSocket client
|
||||
#
|
||||
|
||||
327
src/BLE.cpp
327
src/BLE.cpp
@@ -1,19 +1,16 @@
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/task.h"
|
||||
#include "BLE.hpp"
|
||||
#include "NimBLEDevice.h"
|
||||
#include "WiFi.hpp"
|
||||
#include "nvs_flash.h"
|
||||
#include "socketIO.hpp"
|
||||
#include "defines.h"
|
||||
#include <mutex>
|
||||
#include "bmHTTP.hpp"
|
||||
#include "setup.hpp"
|
||||
#include "esp_mac.h"
|
||||
#include "esp_netif.h"
|
||||
|
||||
|
||||
std::atomic<bool> flag_scan_requested{false};
|
||||
std::atomic<bool> credsGiven{false};
|
||||
std::atomic<bool> tokenGiven{false};
|
||||
std::atomic<bool> isBLEClientConnected{false};
|
||||
std::atomic<bool> scanBlock{false};
|
||||
std::atomic<bool> finalAuth{false};
|
||||
@@ -28,24 +25,13 @@ static std::string UNAME = "";
|
||||
// Global pointers to characteristics for notification support
|
||||
std::atomic<NimBLECharacteristic*> ssidListChar = nullptr;
|
||||
std::atomic<NimBLECharacteristic*> connectConfirmChar = nullptr;
|
||||
|
||||
// Forward declarations
|
||||
bool attemptUseWiFiCreds();
|
||||
bool tokenCheck();
|
||||
std::atomic<NimBLECharacteristic*> authConfirmChar = nullptr;
|
||||
std::atomic<NimBLECharacteristic*> credsChar = nullptr;
|
||||
std::atomic<NimBLECharacteristic*> tokenChar = nullptr;
|
||||
std::atomic<NimBLECharacteristic*> ssidRefreshChar = nullptr;
|
||||
std::atomic<NimBLECharacteristic*> deviceInfoChar = nullptr;
|
||||
|
||||
static QueueHandle_t BLE_event_queue = NULL;
|
||||
static TaskHandle_t BLE_manager_task_handle = NULL;
|
||||
|
||||
SemaphoreHandle_t BLE_Queue_Shutdown_Semaphore = NULL;
|
||||
|
||||
NimBLEAdvertising* initBLE() {
|
||||
BLE_event_queue = xQueueCreate(10, sizeof(BLE_event_type_t));
|
||||
xTaskCreate(BLE_manager_task, "BLE", 8192, NULL, 5, &BLE_manager_task_handle);
|
||||
finalAuth = false;
|
||||
|
||||
NimBLEDevice::init("BlindMaster-C6");
|
||||
@@ -157,18 +143,126 @@ void notifyAuthStatus(bool success) {
|
||||
tmpConfChar->notify();
|
||||
tmpConfChar->setValue(""); // Clear value after notify
|
||||
}
|
||||
|
||||
bool BLEtick(NimBLEAdvertising* pAdvertising) {
|
||||
printf("BleTick\n");
|
||||
if(flag_scan_requested) {
|
||||
flag_scan_requested = false;
|
||||
if (!scanBlock) {
|
||||
scanBlock = true;
|
||||
printf("Scanning WiFi...\n");
|
||||
bmWiFi.scanAndUpdateSSIDList();
|
||||
}
|
||||
else printf("Duplicate scan request\n");
|
||||
}
|
||||
else if (credsGiven) {
|
||||
std::string tmpSSID;
|
||||
std::string tmpUNAME;
|
||||
std::string tmpPASS;
|
||||
wifi_auth_mode_t tmpAUTH;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(dataMutex);
|
||||
tmpSSID = SSID;
|
||||
tmpUNAME = UNAME;
|
||||
tmpPASS = PASS;
|
||||
tmpAUTH = auth;
|
||||
credsGiven = false;
|
||||
}
|
||||
|
||||
bool wifiConnect;
|
||||
if (tmpAUTH == WIFI_AUTH_WPA2_ENTERPRISE || tmpAUTH == WIFI_AUTH_WPA3_ENTERPRISE)
|
||||
wifiConnect = bmWiFi.attemptConnect(tmpSSID.c_str(), tmpUNAME.c_str(), tmpPASS.c_str(), tmpAUTH);
|
||||
else wifiConnect = bmWiFi.attemptConnect(tmpSSID.c_str(), tmpPASS.c_str(), tmpAUTH);
|
||||
if (!wifiConnect) {
|
||||
// notify errored
|
||||
notifyConnectionStatus(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
nvs_handle_t WiFiHandle;
|
||||
esp_err_t err = nvs_open(nvsWiFi, NVS_READWRITE, &WiFiHandle);
|
||||
if (err != ESP_OK) {
|
||||
printf("ERROR Saving Credentials\n");
|
||||
// notify errored
|
||||
notifyConnectionStatus(false);
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
err = nvs_set_str(WiFiHandle, ssidTag, tmpSSID.c_str());
|
||||
if (err == ESP_OK) err = nvs_set_str(WiFiHandle, passTag, tmpPASS.c_str());
|
||||
if (err == ESP_OK) err = nvs_set_str(WiFiHandle, unameTag, tmpUNAME.c_str());
|
||||
if (err == ESP_OK) err = nvs_set_u8(WiFiHandle, authTag, (uint8_t)tmpAUTH);
|
||||
if (err == ESP_OK) nvs_commit(WiFiHandle);
|
||||
nvs_close(WiFiHandle);
|
||||
}
|
||||
if (err == ESP_OK) {
|
||||
// notify connected
|
||||
notifyConnectionStatus(true);
|
||||
}
|
||||
else {
|
||||
// notify connected
|
||||
notifyConnectionStatus(false);
|
||||
}
|
||||
}
|
||||
else if (tokenGiven) {
|
||||
tokenGiven = false;
|
||||
if (!bmWiFi.isConnected()) {
|
||||
printf("ERROR: token given without WiFi connection\n");
|
||||
notifyAuthStatus(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
// HTTP request to verify device with token
|
||||
std::string tmpTOKEN;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(dataMutex);
|
||||
tmpTOKEN = TOKEN;
|
||||
}
|
||||
|
||||
cJSON *responseRoot;
|
||||
bool success = httpGET("verify_device", tmpTOKEN, responseRoot);
|
||||
if (!success) return false;
|
||||
success = false;
|
||||
|
||||
if (responseRoot != NULL) {
|
||||
cJSON *tokenItem = cJSON_GetObjectItem(responseRoot, "token");
|
||||
if (cJSON_IsString(tokenItem) && tokenItem->valuestring != NULL) {
|
||||
printf("New token received: %s\n", tokenItem->valuestring);
|
||||
|
||||
// Save token to NVS
|
||||
nvs_handle_t AuthHandle;
|
||||
esp_err_t nvs_err = nvs_open(nvsAuth, NVS_READWRITE, &AuthHandle);
|
||||
if (nvs_err == ESP_OK) {
|
||||
nvs_err = nvs_set_str(AuthHandle, tokenTag, tokenItem->valuestring);
|
||||
if (nvs_err == ESP_OK) {
|
||||
nvs_commit(AuthHandle);
|
||||
success = true;
|
||||
webToken = tokenItem->valuestring;
|
||||
}
|
||||
else printf("ERROR: could not save webToken to NVS\n");
|
||||
nvs_close(AuthHandle);
|
||||
}
|
||||
else printf("ERROR: Couldn't open NVS for auth token\n");
|
||||
}
|
||||
cJSON_Delete(responseRoot);
|
||||
}
|
||||
else printf("Failed to parse JSON response\n");
|
||||
|
||||
finalAuth = true;
|
||||
notifyAuthStatus(success);
|
||||
if (success) NimBLEDevice::deinit(true); // deinitialize BLE
|
||||
return success;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
BLE_Queue_Shutdown_Semaphore = xSemaphoreCreateBinary();
|
||||
BLE_event_type_t event_type = EVENT_SHUTDOWN;
|
||||
xQueueSend(BLE_event_queue, &event_type, portMAX_DELAY);
|
||||
xSemaphoreTake(BLE_Queue_Shutdown_Semaphore, portMAX_DELAY);
|
||||
vQueueDelete(BLE_event_queue);
|
||||
esp_wifi_scan_stop();
|
||||
if (!finalAuth) esp_wifi_disconnect();
|
||||
scanBlock = false;
|
||||
vSemaphoreDelete(BLE_Queue_Shutdown_Semaphore);
|
||||
BLE_event_queue = xQueueCreate(10, sizeof(BLE_event_type_t));
|
||||
xTaskCreate(BLE_manager_task, "BLE", 8192, NULL, 5, &BLE_manager_task_handle);
|
||||
flag_scan_requested = false;
|
||||
credsGiven = false;
|
||||
tokenGiven = false;
|
||||
}
|
||||
|
||||
void MyServerCallbacks::onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo) {
|
||||
@@ -180,7 +274,7 @@ void MyServerCallbacks::onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInf
|
||||
void MyServerCallbacks::onDisconnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, int reason) {
|
||||
isBLEClientConnected = false;
|
||||
printf("Client disconnected - reason: %d\n", reason);
|
||||
if (!finalAuth) reset();
|
||||
reset();
|
||||
}
|
||||
|
||||
void MyCharCallbacks::onRead(NimBLECharacteristic* pChar, NimBLEConnInfo& connInfo) {
|
||||
@@ -224,6 +318,7 @@ void MyCharCallbacks::onWrite(NimBLECharacteristic* pChar, NimBLEConnInfo& connI
|
||||
else error = true;
|
||||
if (error) {
|
||||
printf("ERROR: Invalid Auth mode passed in with JSON.\n");
|
||||
credsGiven = false;
|
||||
cJSON_Delete(root);
|
||||
return;
|
||||
}
|
||||
@@ -236,47 +331,35 @@ void MyCharCallbacks::onWrite(NimBLECharacteristic* pChar, NimBLEConnInfo& connI
|
||||
|
||||
if (tempCredsGiven) {
|
||||
printf("Received credentials, will attempt connection\n");
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(dataMutex);
|
||||
|
||||
auth = (wifi_auth_mode_t)(authType->valueint);
|
||||
SSID = ssid->valuestring;
|
||||
PASS = passPresent ? password->valuestring : "";
|
||||
UNAME = unamePresent ? uname->valuestring : "";
|
||||
}
|
||||
BLE_event_type_t event_type = EVENT_CREDS_GIVEN;
|
||||
if (xQueueSend(BLE_event_queue, &event_type, pdMS_TO_TICKS(10)) == pdPASS)
|
||||
printf("Successfully added credsGiven to event queue\n");
|
||||
else printf("CredsGiven event queue addition failed\n");
|
||||
credsGiven = tempCredsGiven; // update the global flag.
|
||||
}
|
||||
else printf("ERROR: Did not receive necessary credentials.\n");
|
||||
cJSON_Delete(root);
|
||||
} else {
|
||||
printf("Failed to parse JSON\n");
|
||||
credsGiven = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (pChar == currentTokenChar) {
|
||||
if (val.length() > 0) {
|
||||
printf("Received Token: %s\n", val.c_str());
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(dataMutex);
|
||||
TOKEN = val;
|
||||
}
|
||||
BLE_event_type_t event_type = EVENT_TOKEN_GIVEN;
|
||||
if (xQueueSend(BLE_event_queue, &event_type, pdMS_TO_TICKS(10)) == pdPASS)
|
||||
printf("Successfully added tokenGiven to event queue\n");
|
||||
else printf("TokenGiven event queue addition failed\n");
|
||||
tokenGiven = true;
|
||||
}
|
||||
}
|
||||
else if (pChar == currentRefreshChar) {
|
||||
if (val == "Start") {
|
||||
// Refresh characteristic
|
||||
BLE_event_type_t event_type = EVENT_SCAN_REQUESTED;
|
||||
if (xQueueSend(BLE_event_queue, &event_type, pdMS_TO_TICKS(10)) == pdPASS) {
|
||||
printf("Event queue addition success for scan start\n");
|
||||
}
|
||||
else printf("Scan start event queue addition failed\n");
|
||||
printf("Refresh Requested\n");
|
||||
flag_scan_requested = true;
|
||||
}
|
||||
else if (val == "Done") {
|
||||
printf("Data read complete\n");
|
||||
@@ -284,162 +367,4 @@ void MyCharCallbacks::onWrite(NimBLECharacteristic* pChar, NimBLEConnInfo& connI
|
||||
}
|
||||
}
|
||||
else printf("Unknown UUID: %s\n", uuidStr.c_str());
|
||||
}
|
||||
|
||||
void BLE_manager_task(void *pvParameters) {
|
||||
BLE_event_type_t received_event_type;
|
||||
|
||||
while (true) {
|
||||
if (xQueueReceive(BLE_event_queue, &received_event_type, portMAX_DELAY)) {
|
||||
if (received_event_type == EVENT_SCAN_REQUESTED) {
|
||||
printf("Refresh Requested\n");
|
||||
if (!scanBlock) {
|
||||
scanBlock = true;
|
||||
printf("Scanning WiFi...\n");
|
||||
WiFi::scanAndUpdateSSIDList();
|
||||
}
|
||||
else printf("Duplicate scan request\n");
|
||||
}
|
||||
else if (received_event_type == EVENT_TOKEN_GIVEN) {
|
||||
if (tokenCheck()) {
|
||||
vQueueDelete(BLE_event_queue);
|
||||
if (setupTaskHandle != NULL) {
|
||||
xTaskNotifyGive(setupTaskHandle);
|
||||
printf("Setup complete.\n");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (received_event_type == EVENT_CREDS_GIVEN)
|
||||
attemptUseWiFiCreds();
|
||||
else if (received_event_type == EVENT_SHUTDOWN) break;
|
||||
}
|
||||
}
|
||||
xSemaphoreGive(BLE_Queue_Shutdown_Semaphore); // this is null-safe
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
bool tokenCheck() {
|
||||
if (!WiFi::isConnected()) {
|
||||
printf("ERROR: token given without WiFi connection\n");
|
||||
notifyAuthStatus(false);
|
||||
return false;
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(500));
|
||||
|
||||
// --- DIAGNOSTIC: snapshot network state before HTTP ---
|
||||
{
|
||||
extern esp_netif_t* _diag_netif __attribute__((weak));
|
||||
// Use esp_netif_get_handle_from_ifkey to get the STA netif without changing headers
|
||||
esp_netif_t* sta_netif = esp_netif_get_handle_from_ifkey("WIFI_STA_DEF");
|
||||
if (sta_netif) {
|
||||
esp_netif_ip_info_t ip_info;
|
||||
esp_netif_get_ip_info(sta_netif, &ip_info);
|
||||
printf("[DIAG tokenCheck] IP: " IPSTR "\n", IP2STR(&ip_info.ip));
|
||||
|
||||
esp_netif_dns_info_t dns;
|
||||
if (esp_netif_get_dns_info(sta_netif, ESP_NETIF_DNS_MAIN, &dns) == ESP_OK)
|
||||
printf("[DIAG tokenCheck] DNS: " IPSTR "\n", IP2STR(&dns.ip.u_addr.ip4));
|
||||
else
|
||||
printf("[DIAG tokenCheck] DNS: NOT SET\n");
|
||||
} else {
|
||||
printf("[DIAG tokenCheck] Could not get STA netif handle\n");
|
||||
}
|
||||
printf("[DIAG tokenCheck] WiFi::isConnected() = %d\n", (int)WiFi::isConnected());
|
||||
}
|
||||
|
||||
// HTTP request to verify device with token
|
||||
std::string tmpTOKEN;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(dataMutex);
|
||||
tmpTOKEN = TOKEN;
|
||||
}
|
||||
|
||||
cJSON *responseRoot;
|
||||
bool success = httpGET("verify_device", tmpTOKEN, responseRoot);
|
||||
if (!success) {
|
||||
notifyAuthStatus(false);
|
||||
return false;
|
||||
}
|
||||
success = false;
|
||||
|
||||
if (responseRoot != NULL) {
|
||||
cJSON *tokenItem = cJSON_GetObjectItem(responseRoot, "token");
|
||||
if (cJSON_IsString(tokenItem) && tokenItem->valuestring != NULL) {
|
||||
printf("New token received: %s\n", tokenItem->valuestring);
|
||||
|
||||
// Save token to NVS
|
||||
nvs_handle_t AuthHandle;
|
||||
esp_err_t nvs_err = nvs_open(nvsAuth, NVS_READWRITE, &AuthHandle);
|
||||
if (nvs_err == ESP_OK) {
|
||||
nvs_err = nvs_set_str(AuthHandle, tokenTag, tokenItem->valuestring);
|
||||
if (nvs_err == ESP_OK) {
|
||||
nvs_commit(AuthHandle);
|
||||
success = true;
|
||||
webToken = tokenItem->valuestring;
|
||||
}
|
||||
else printf("ERROR: could not save webToken to NVS\n");
|
||||
nvs_close(AuthHandle);
|
||||
}
|
||||
else printf("ERROR: Couldn't open NVS for auth token\n");
|
||||
}
|
||||
cJSON_Delete(responseRoot);
|
||||
}
|
||||
else printf("Failed to parse JSON response\n");
|
||||
|
||||
finalAuth = true;
|
||||
notifyAuthStatus(success);
|
||||
if (success) NimBLEDevice::deinit(true); // deinitialize BLE
|
||||
return success;
|
||||
}
|
||||
|
||||
bool attemptUseWiFiCreds() {
|
||||
std::string tmpSSID;
|
||||
std::string tmpUNAME;
|
||||
std::string tmpPASS;
|
||||
wifi_auth_mode_t tmpAUTH;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(dataMutex);
|
||||
tmpSSID = SSID;
|
||||
tmpUNAME = UNAME;
|
||||
tmpPASS = PASS;
|
||||
tmpAUTH = auth;
|
||||
}
|
||||
|
||||
bool wifiConnect;
|
||||
if (tmpAUTH == WIFI_AUTH_WPA2_ENTERPRISE || tmpAUTH == WIFI_AUTH_WPA3_ENTERPRISE)
|
||||
wifiConnect = WiFi::attemptConnect(tmpSSID.c_str(), tmpUNAME.c_str(), tmpPASS.c_str(), tmpAUTH);
|
||||
else wifiConnect = WiFi::attemptConnect(tmpSSID.c_str(), tmpPASS.c_str(), tmpAUTH);
|
||||
if (!wifiConnect) {
|
||||
// notify errored
|
||||
notifyConnectionStatus(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
nvs_handle_t WiFiHandle;
|
||||
esp_err_t err = nvs_open(nvsWiFi, NVS_READWRITE, &WiFiHandle);
|
||||
if (err != ESP_OK) {
|
||||
printf("ERROR Saving Credentials\n");
|
||||
// notify errored
|
||||
notifyConnectionStatus(false);
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
err = nvs_set_str(WiFiHandle, ssidTag, tmpSSID.c_str());
|
||||
if (err == ESP_OK) err = nvs_set_str(WiFiHandle, passTag, tmpPASS.c_str());
|
||||
if (err == ESP_OK) err = nvs_set_str(WiFiHandle, unameTag, tmpUNAME.c_str());
|
||||
if (err == ESP_OK) err = nvs_set_u8(WiFiHandle, authTag, (uint8_t)tmpAUTH);
|
||||
if (err == ESP_OK) nvs_commit(WiFiHandle);
|
||||
nvs_close(WiFiHandle);
|
||||
}
|
||||
if (err == ESP_OK) {
|
||||
// notify connected
|
||||
notifyConnectionStatus(true);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
// notify errored
|
||||
notifyConnectionStatus(false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
# This file was automatically generated for projects
|
||||
# without default 'CMakeLists.txt' file.
|
||||
|
||||
FILE(GLOB_RECURSE app_sources ${CMAKE_SOURCE_DIR}/src/*.* ${CMAKE_SOURCE_DIR}/test/*.*)
|
||||
FILE(GLOB_RECURSE app_sources ${CMAKE_SOURCE_DIR}/src/*.*)
|
||||
|
||||
idf_component_register(SRCS ${app_sources}
|
||||
INCLUDE_DIRS "." "${CMAKE_SOURCE_DIR}/test"
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES nvs_flash esp-nimble-cpp esp_socketio_client)
|
||||
|
||||
117
src/WiFi.cpp
117
src/WiFi.cpp
@@ -2,11 +2,8 @@
|
||||
#include "esp_eap_client.h"
|
||||
#include "cJSON.h" // Native replacement for ArduinoJson
|
||||
#include "BLE.hpp"
|
||||
#include "esp_wifi_he.h"
|
||||
#include "esp_netif.h"
|
||||
#include "lwip/dns.h"
|
||||
|
||||
TaskHandle_t WiFi::awaitConnectHandle = NULL;
|
||||
std::atomic<bool> WiFi::authFailed{false};
|
||||
EventGroupHandle_t WiFi::s_wifi_event_group = NULL;
|
||||
esp_netif_t* WiFi::netif = NULL;
|
||||
esp_event_handler_instance_t WiFi::instance_any_id = NULL;
|
||||
@@ -15,6 +12,8 @@ esp_event_handler_instance_t WiFi::instance_got_ip = NULL;
|
||||
#define WIFI_CONNECTED_BIT BIT0
|
||||
#define WIFI_STARTED_BIT BIT1
|
||||
|
||||
WiFi bmWiFi;
|
||||
|
||||
// The Event Handler (The engine room)
|
||||
void WiFi::event_handler(void* arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void* event_data) {
|
||||
@@ -39,33 +38,26 @@ void WiFi::event_handler(void* arg, esp_event_base_t event_base,
|
||||
case WIFI_REASON_AUTH_FAIL: // Reason 202
|
||||
case WIFI_REASON_HANDSHAKE_TIMEOUT: // Reason 204
|
||||
printf("ERROR: Likely Wrong Password!\n");
|
||||
if (awaitConnectHandle != NULL) {
|
||||
xTaskNotify(awaitConnectHandle, false, eSetValueWithOverwrite);
|
||||
}
|
||||
authFailed = true;
|
||||
break;
|
||||
|
||||
case WIFI_REASON_NO_AP_FOUND: // Reason 201
|
||||
printf("ERROR: SSID Not Found\n");
|
||||
if (awaitConnectHandle != NULL) {
|
||||
xTaskNotify(awaitConnectHandle, false, eSetValueWithOverwrite);
|
||||
}
|
||||
authFailed = true;
|
||||
break;
|
||||
|
||||
case WIFI_REASON_ASSOC_LEAVE: // Reason 8 - Manual disconnect
|
||||
printf("Manual disconnect, not retrying\n");
|
||||
esp_netif_dhcpc_start(netif);
|
||||
break;
|
||||
|
||||
case WIFI_REASON_ASSOC_FAIL: // Reason 203 (Can be AP busy/rate limiting)
|
||||
printf("Association failed, will retry...\n");
|
||||
vTaskDelay(pdMS_TO_TICKS(1000)); // Wait 1 second before retry to avoid rate limiting
|
||||
esp_netif_dhcpc_start(netif);
|
||||
esp_wifi_connect();
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("Retrying...\n");
|
||||
esp_netif_dhcpc_start(netif);
|
||||
esp_wifi_connect();
|
||||
break;
|
||||
}
|
||||
@@ -83,9 +75,6 @@ void WiFi::event_handler(void* arg, esp_event_base_t event_base,
|
||||
ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
|
||||
printf("Got IP: " IPSTR "\n", IP2STR(&event->ip_info.ip));
|
||||
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
|
||||
if (awaitConnectHandle != NULL) {
|
||||
xTaskNotify(awaitConnectHandle, true, eSetValueWithOverwrite);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,7 +110,6 @@ void WiFi::init() {
|
||||
|
||||
ESP_ERROR_CHECK(esp_wifi_start());
|
||||
xEventGroupWaitBits(s_wifi_event_group, WIFI_STARTED_BIT, pdFALSE, pdTRUE, portMAX_DELAY);
|
||||
awaitConnectHandle = NULL;
|
||||
}
|
||||
|
||||
// --- CHECK STATUS ---
|
||||
@@ -184,59 +172,19 @@ bool WiFi::attemptConnect(const std::string ssid, const std::string uname,
|
||||
}
|
||||
|
||||
bool WiFi::awaitConnected() {
|
||||
awaitConnectHandle = xTaskGetCurrentTaskHandle();
|
||||
if (esp_wifi_connect() != ESP_OK) {
|
||||
awaitConnectHandle = NULL;
|
||||
return false;
|
||||
}
|
||||
authFailed = false;
|
||||
if (esp_wifi_connect() != ESP_OK) return false;
|
||||
|
||||
uint32_t status;
|
||||
uint8_t MAX_TIMEOUT = 10; //seconds
|
||||
if (xTaskNotifyWait(0, ULONG_MAX, &status, pdMS_TO_TICKS(MAX_TIMEOUT * 1000)) == pdTRUE) {
|
||||
awaitConnectHandle = NULL;
|
||||
if (!status) {
|
||||
uint8_t attempts = 0;
|
||||
while (!isConnected() && attempts < 20) {
|
||||
if (authFailed) {
|
||||
printf("SSID/Password was wrong! Aborting connection attempt.\n");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// Timeout - check if connected anyway
|
||||
awaitConnectHandle = NULL;
|
||||
}
|
||||
|
||||
if (isConnected()) {
|
||||
// --- DIAGNOSTIC: snapshot network state before DHCP stop ---
|
||||
// Save IP and DNS before stopping DHCP — dhcpc_stop clears both
|
||||
esp_netif_ip_info_t ip_info;
|
||||
esp_netif_get_ip_info(netif, &ip_info);
|
||||
esp_netif_dns_info_t dns_main, dns_backup;
|
||||
esp_netif_get_dns_info(netif, ESP_NETIF_DNS_MAIN, &dns_main);
|
||||
esp_netif_get_dns_info(netif, ESP_NETIF_DNS_BACKUP, &dns_backup);
|
||||
|
||||
esp_netif_dhcpc_stop(netif);
|
||||
|
||||
// Re-apply IP and DNS as static configuration
|
||||
esp_netif_set_ip_info(netif, &ip_info);
|
||||
esp_netif_set_dns_info(netif, ESP_NETIF_DNS_MAIN, &dns_main);
|
||||
esp_netif_set_dns_info(netif, ESP_NETIF_DNS_BACKUP, &dns_backup);
|
||||
uint8_t protocol_bitmap = 0;
|
||||
esp_err_t err = esp_wifi_get_protocol(WIFI_IF_STA, &protocol_bitmap);
|
||||
|
||||
if (err == ESP_OK && (protocol_bitmap & WIFI_PROTOCOL_11AX)) {
|
||||
// WiFi 6 (802.11ax) - Use Target Wake Time (TWT) for power saving
|
||||
wifi_twt_setup_config_t twt_config = {
|
||||
.setup_cmd = TWT_REQUEST,
|
||||
.trigger = true,
|
||||
.flow_type = 0, // Announced
|
||||
.flow_id = 0,
|
||||
.wake_invl_expn = 12, // Exponent for interval
|
||||
.min_wake_dura = 255, // ~65ms (unit is 256 microseconds)
|
||||
.wake_invl_mant = 14648, // Mantissa (mant * 2^exp = 60,000,000 us = 60s)
|
||||
.timeout_time_ms = 5000,
|
||||
};
|
||||
esp_wifi_sta_itwt_setup(&twt_config);
|
||||
}
|
||||
return true;
|
||||
vTaskDelay(500);
|
||||
attempts++;
|
||||
}
|
||||
if (isConnected()) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -244,6 +192,8 @@ bool WiFi::awaitConnected() {
|
||||
void WiFi::scanAndUpdateSSIDList() {
|
||||
printf("Starting WiFi Scan...\n");
|
||||
|
||||
// 1. Start Scan (Blocking Mode = true)
|
||||
// In blocking mode, this function waits here until scan is done (~2 seconds)
|
||||
esp_wifi_sta_enterprise_disable();
|
||||
esp_wifi_disconnect();
|
||||
|
||||
@@ -274,13 +224,7 @@ void WiFi::processScanResults() {
|
||||
printf("Heap allocation error in processScanResults\n");
|
||||
return;
|
||||
}
|
||||
|
||||
esp_err_t err = esp_wifi_scan_get_ap_records(&ap_count, ap_list);
|
||||
if (err != ESP_OK) {
|
||||
printf("Failed to get scan records\n");
|
||||
free(ap_list);
|
||||
return;
|
||||
}
|
||||
ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&ap_count, ap_list));
|
||||
|
||||
// 3. Build JSON using cJSON
|
||||
cJSON *root = cJSON_CreateArray();
|
||||
@@ -313,32 +257,3 @@ void WiFi::processScanResults() {
|
||||
cJSON_Delete(root); // This deletes all children (items) too
|
||||
free(json_string); // cJSON_Print allocates memory, you must free it
|
||||
}
|
||||
|
||||
bool WiFi::attemptDHCPrenewal() {
|
||||
xEventGroupClearBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
|
||||
esp_netif_dhcpc_start(netif);
|
||||
EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
|
||||
WIFI_CONNECTED_BIT,
|
||||
pdFALSE,
|
||||
pdFALSE,
|
||||
pdMS_TO_TICKS(4000));
|
||||
|
||||
if (bits & WIFI_CONNECTED_BIT) {
|
||||
printf("renewal success\n");
|
||||
esp_netif_ip_info_t ip_info;
|
||||
esp_netif_get_ip_info(netif, &ip_info);
|
||||
esp_netif_dns_info_t dns_main, dns_backup;
|
||||
esp_netif_get_dns_info(netif, ESP_NETIF_DNS_MAIN, &dns_main);
|
||||
esp_netif_get_dns_info(netif, ESP_NETIF_DNS_BACKUP, &dns_backup);
|
||||
|
||||
esp_netif_dhcpc_stop(netif);
|
||||
|
||||
esp_netif_set_ip_info(netif, &ip_info);
|
||||
esp_netif_set_dns_info(netif, ESP_NETIF_DNS_MAIN, &dns_main);
|
||||
esp_netif_set_dns_info(netif, ESP_NETIF_DNS_BACKUP, &dns_backup);
|
||||
return true;
|
||||
}
|
||||
printf("DHCP Renewal failed. Reconnecting Wi-Fi...\n");
|
||||
esp_wifi_disconnect();
|
||||
return awaitConnected();
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
|
||||
// // 3. Post Event to System Loop
|
||||
// battery_data_t data = { .soc = soc, .voltage = voltage };
|
||||
|
||||
// esp_event_post(BATTERY_EVENTS, BATTERY_EVENT_UPDATE, &data, sizeof(data), 0);
|
||||
|
||||
// // Optional: Post warnings
|
||||
// if (soc < 20.0) {
|
||||
// esp_event_post(BATTERY_EVENTS, BATTERY_EVENT_LOW, NULL, 0, 0);
|
||||
// }
|
||||
@@ -63,59 +63,6 @@ bool httpGET(std::string endpoint, std::string token, cJSON* &JSONresponse) {
|
||||
return success;
|
||||
}
|
||||
|
||||
bool httpPOST(std::string endpoint, std::string token, cJSON* postData, cJSON* &JSONresponse) {
|
||||
std::string url = urlBase + endpoint;
|
||||
std::string responseBuffer = "";
|
||||
|
||||
// Convert JSON object to string
|
||||
char* postString = cJSON_PrintUnformatted(postData);
|
||||
if (postString == NULL) {
|
||||
printf("Failed to serialize JSON for POST\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
esp_http_client_config_t config = {};
|
||||
config.url = url.c_str();
|
||||
config.method = HTTP_METHOD_POST;
|
||||
config.event_handler = _http_event_handler;
|
||||
config.user_data = &responseBuffer;
|
||||
if (secureSrv) {
|
||||
config.transport_type = HTTP_TRANSPORT_OVER_SSL;
|
||||
config.crt_bundle_attach = esp_crt_bundle_attach;
|
||||
}
|
||||
|
||||
esp_http_client_handle_t client = esp_http_client_init(&config);
|
||||
|
||||
// Set headers
|
||||
std::string authHeader = "Bearer " + token;
|
||||
esp_http_client_set_header(client, "Authorization", authHeader.c_str());
|
||||
esp_http_client_set_header(client, "Content-Type", "application/json");
|
||||
|
||||
// Set POST data
|
||||
esp_http_client_set_post_field(client, postString, strlen(postString));
|
||||
|
||||
// Perform request
|
||||
esp_err_t err = esp_http_client_perform(client);
|
||||
bool success = false;
|
||||
|
||||
if (err == ESP_OK) {
|
||||
int status_code = esp_http_client_get_status_code(client);
|
||||
printf("Status = %d, Content Length = %d\n", status_code, esp_http_client_get_content_length(client));
|
||||
|
||||
if (status_code == 200 || status_code == 201) {
|
||||
printf("Response: %s\n", responseBuffer.c_str());
|
||||
JSONresponse = cJSON_Parse(responseBuffer.c_str());
|
||||
if (JSONresponse) success = true;
|
||||
}
|
||||
} else {
|
||||
printf("HTTP POST failed: %s\n", esp_err_to_name(err));
|
||||
}
|
||||
|
||||
free(postString);
|
||||
esp_http_client_cleanup(client);
|
||||
return success;
|
||||
}
|
||||
|
||||
void deleteWiFiAndTokenDetails() {
|
||||
nvs_handle_t wifiHandle;
|
||||
if (nvs_open(nvsWiFi, NVS_READWRITE, &wifiHandle) == ESP_OK) {
|
||||
|
||||
@@ -1,16 +1,6 @@
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "calibration.hpp"
|
||||
#include "defines.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "socketIO.hpp"
|
||||
#include <limits.h>
|
||||
|
||||
// Static member definitions
|
||||
std::atomic<bool> Calibration::calibrated{false};
|
||||
std::atomic<int32_t> Calibration::UpTicks{0};
|
||||
std::atomic<int32_t> Calibration::DownTicks{0};
|
||||
TaskHandle_t calibTaskHandle = NULL;
|
||||
|
||||
void Calibration::init() {
|
||||
nvs_handle_t calibHandle;
|
||||
@@ -112,7 +102,7 @@ bool Calibration::completeCalib(Encoder& topEnc) {
|
||||
|
||||
int32_t Calibration::convertToTicks(uint8_t appPos) {
|
||||
// appPos between 0 and 10, convert to target encoder ticks.
|
||||
return (((int32_t)appPos * (UpTicks - DownTicks)) / 10) + DownTicks + ((UpTicks - DownTicks) / 20);
|
||||
return (((int32_t)appPos * (UpTicks - DownTicks)) / 10) + DownTicks;
|
||||
}
|
||||
|
||||
uint8_t Calibration::convertToAppPos(int32_t ticks) {
|
||||
@@ -120,50 +110,3 @@ uint8_t Calibration::convertToAppPos(int32_t ticks) {
|
||||
int8_t retVal = (ticks - DownTicks) * 10 / (UpTicks - DownTicks);
|
||||
return (retVal < 0) ? 0 : ((retVal > 10) ? 10 : retVal);
|
||||
}
|
||||
|
||||
bool calibrate() {
|
||||
calibTaskHandle = xTaskGetCurrentTaskHandle();
|
||||
printf("Connecting to Socket.IO server for calibration...\n");
|
||||
initSocketIO();
|
||||
|
||||
// Wait for device_init message from server with timeout
|
||||
int timeout_count = 0;
|
||||
const int MAX_TIMEOUT = 60; // seconds
|
||||
|
||||
uint32_t status;
|
||||
// Wait for notification with timeout
|
||||
if (xTaskNotifyWait(0, ULONG_MAX, &status, pdMS_TO_TICKS(MAX_TIMEOUT * 1000)) == pdTRUE) {
|
||||
// Notification received within timeout
|
||||
if (status) {
|
||||
printf("Connected successfully, awaiting destroy command\n");
|
||||
// Socket is now authenticated. Tell the server this device is not
|
||||
// calibrated so it resets DB state and notifies the app to show the
|
||||
// pre-calibration screen. The device stays connected, so when the user
|
||||
// taps Calibrate the subsequent calib_start event will arrive here.
|
||||
emitCalibStatus(false, 1);
|
||||
xTaskNotifyWait(0, ULONG_MAX, &status, portMAX_DELAY);
|
||||
calibTaskHandle = NULL;
|
||||
if (status == 2) { // calibration complete
|
||||
printf("Calibration process complete\n");
|
||||
stopSocketIO();
|
||||
return true;
|
||||
}
|
||||
else { // unexpected disconnect
|
||||
printf("Disconnected unexpectedly!\n");
|
||||
stopSocketIO();
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
calibTaskHandle = NULL;
|
||||
printf("Connection failed! Returning to setup.\n");
|
||||
stopSocketIO();
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// Timeout reached
|
||||
calibTaskHandle = NULL;
|
||||
printf("Timeout waiting for device_init - connection failed\n");
|
||||
stopSocketIO();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -51,8 +51,8 @@ void IRAM_ATTR Encoder::isr_handler(void* arg)
|
||||
if (calibListen) servoCalibListen();
|
||||
if (encoder->feedWDog) {
|
||||
esp_timer_stop(encoder->watchdog_handle);
|
||||
esp_timer_start_once(encoder->watchdog_handle, 2000000);
|
||||
// debugLEDTgl();
|
||||
esp_timer_start_once(encoder->watchdog_handle, 500000);
|
||||
debugLEDTgl();
|
||||
}
|
||||
if (encoder->wandListen) servoWandListen();
|
||||
if (encoder->serverListen) servoServerListen();
|
||||
@@ -63,8 +63,8 @@ void IRAM_ATTR Encoder::isr_handler(void* arg)
|
||||
if (calibListen) servoCalibListen();
|
||||
if (encoder->feedWDog) {
|
||||
esp_timer_stop(encoder->watchdog_handle);
|
||||
esp_timer_start_once(encoder->watchdog_handle, 2000000);
|
||||
// debugLEDTgl();
|
||||
esp_timer_start_once(encoder->watchdog_handle, 500000);
|
||||
debugLEDTgl();
|
||||
}
|
||||
if (encoder->wandListen) servoWandListen();
|
||||
if (encoder->serverListen) servoServerListen();
|
||||
@@ -112,7 +112,7 @@ void Encoder::setupWatchdog() {
|
||||
ESP_ERROR_CHECK(esp_timer_create(&enc_watchdog_args, &watchdog_handle));
|
||||
}
|
||||
|
||||
ESP_ERROR_CHECK(esp_timer_start_once(watchdog_handle, 2000000));
|
||||
ESP_ERROR_CHECK(esp_timer_start_once(watchdog_handle, 500000));
|
||||
feedWDog = true;
|
||||
}
|
||||
|
||||
|
||||
20
src/i2c.c
20
src/i2c.c
@@ -1,20 +0,0 @@
|
||||
#include "i2c.h"
|
||||
#include "driver/i2c.h"
|
||||
#include "esp_log.h"
|
||||
#include "max17048.h"
|
||||
|
||||
// Helper: Initializes I2C controller at 100kHz, returns 0=success
|
||||
esp_err_t i2c_init() {
|
||||
// 1. Initialize I2C
|
||||
i2c_config_t conf = {
|
||||
.mode = I2C_MODE_MASTER,
|
||||
.sda_io_num = I2C_MASTER_SDA_IO,
|
||||
.scl_io_num = I2C_MASTER_SCL_IO,
|
||||
.sda_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.scl_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.master.clk_speed = I2C_MASTER_FREQ_HZ,
|
||||
};
|
||||
|
||||
return (i2c_param_config(I2C_MASTER_NUM, &conf) ||
|
||||
i2c_driver_install(I2C_MASTER_NUM, conf.mode, 0, 0, 0));
|
||||
}
|
||||
@@ -1,4 +1,2 @@
|
||||
dependencies:
|
||||
bubblesnake/esp_socketio_client: "^1.0.0"
|
||||
esp-nimble-cpp:
|
||||
git: https://github.com/h2zero/esp-nimble-cpp.git
|
||||
96
src/main.cpp
96
src/main.cpp
@@ -8,17 +8,27 @@
|
||||
#include "socketIO.hpp"
|
||||
#include "encoder.hpp"
|
||||
#include "calibration.hpp"
|
||||
#include "esp_pm.h"
|
||||
#include "mainEventLoop.hpp"
|
||||
#include "max17048.h"
|
||||
#include "bms_test.hpp"
|
||||
#include "servo_test.hpp"
|
||||
#include "encoder_test.hpp"
|
||||
|
||||
// Global encoder instances
|
||||
Encoder* topEnc = new Encoder(ENCODER_PIN_A, ENCODER_PIN_B);
|
||||
Encoder* bottomEnc = new Encoder(InputEnc_PIN_A, InputEnc_PIN_B);
|
||||
|
||||
// Global encoder pointers (used by servo.cpp)
|
||||
|
||||
// Global calibration instance
|
||||
Calibration calib;
|
||||
|
||||
void switchOnOffServo() {
|
||||
while (1) {
|
||||
printf("Servo On\n");
|
||||
servoOn(CCW, manual);
|
||||
vTaskDelay(pdMS_TO_TICKS(2000));
|
||||
printf("Servo Off\n");
|
||||
servoOff();
|
||||
vTaskDelay(pdMS_TO_TICKS(2000));
|
||||
}
|
||||
}
|
||||
|
||||
void mainApp() {
|
||||
esp_err_t ret = nvs_flash_init(); // change to secure init logic soon!!
|
||||
// 2. If NVS is full or corrupt (common after flashing new code), erase and retry
|
||||
@@ -28,36 +38,72 @@ void mainApp() {
|
||||
}
|
||||
ESP_ERROR_CHECK(ret);
|
||||
|
||||
main_event_queue = xQueueCreate(10, sizeof(main_event_type_t));
|
||||
|
||||
WiFi::init();
|
||||
Calibration::init();
|
||||
bmWiFi.init();
|
||||
calib.init();
|
||||
|
||||
// Initialize encoders
|
||||
topEnc->init();
|
||||
bottomEnc->init();
|
||||
servoInit();
|
||||
max17048_init();
|
||||
printf("beforeSetup\n");
|
||||
setupAndCalibrate();
|
||||
printf("afterSetup\n");
|
||||
|
||||
xTaskCreate(wakeTimer, "wakeTimer", 2048, NULL, 5, &wakeTaskHandle);
|
||||
// switchOnOffServo();
|
||||
|
||||
mainEventLoop();
|
||||
setupLoop();
|
||||
|
||||
statusResolved = false;
|
||||
|
||||
int32_t prevCount = topEnc->getCount();
|
||||
|
||||
// Main loop
|
||||
while (1) {
|
||||
// websocket disconnect/reconnect handling
|
||||
if (statusResolved) {
|
||||
if (!connected) {
|
||||
printf("Disconnected! Beginning setup loop.\n");
|
||||
stopSocketIO();
|
||||
setupLoop();
|
||||
}
|
||||
else printf("Reconnected!\n");
|
||||
statusResolved = false;
|
||||
}
|
||||
|
||||
if (clearCalibFlag) {
|
||||
calib.clearCalibrated();
|
||||
emitCalibStatus(false);
|
||||
clearCalibFlag = false;
|
||||
}
|
||||
if (savePosFlag) {
|
||||
servoSavePos();
|
||||
savePosFlag = false;
|
||||
|
||||
// Send position update to server
|
||||
uint8_t currentAppPos = calib.convertToAppPos(topEnc->getCount());
|
||||
emitPosHit(currentAppPos);
|
||||
|
||||
printf("Sent pos_hit: position %d\n", currentAppPos);
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
}
|
||||
}
|
||||
|
||||
void pm_init() {
|
||||
esp_pm_config_t pm_config = {
|
||||
.max_freq_mhz = 160, // Max CPU frequency
|
||||
.min_freq_mhz = 80, // Min CPU frequency (DFS)
|
||||
.light_sleep_enable = true // ALLOW CPU to power down during idle
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_pm_configure(&pm_config));
|
||||
void encoderTest() {
|
||||
// Create encoder instance
|
||||
Encoder encoder(ENCODER_PIN_A, ENCODER_PIN_B);
|
||||
encoder.init();
|
||||
|
||||
int32_t prevCount = encoder.getCount();
|
||||
|
||||
while (1) {
|
||||
int32_t currentCount = encoder.getCount();
|
||||
if (currentCount != prevCount) {
|
||||
prevCount = currentCount;
|
||||
printf("Encoder Pos: %d\n", prevCount);
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void app_main() {
|
||||
pm_init();
|
||||
mainApp();
|
||||
// servo_test();
|
||||
// encoderTest();
|
||||
}
|
||||
@@ -1,220 +0,0 @@
|
||||
#include "mainEventLoop.hpp"
|
||||
#include "calibration.hpp"
|
||||
#include "setup.hpp"
|
||||
#include "servo.hpp"
|
||||
#include "bmHTTP.hpp"
|
||||
#include "cJSON.h"
|
||||
#include "encoder.hpp"
|
||||
#include "WiFi.hpp"
|
||||
#include "socketIO.hpp"
|
||||
#include "max17048.h"
|
||||
#include "esp_sleep.h"
|
||||
#include "defines.h"
|
||||
|
||||
// Give C linkage so max17048.c (a C translation unit) can link against these
|
||||
extern "C" {
|
||||
TaskHandle_t wakeTaskHandle = NULL;
|
||||
QueueHandle_t main_event_queue = NULL;
|
||||
}
|
||||
|
||||
// ── Battery helpers ───────────────────────────────────────────────────────────
|
||||
|
||||
static const char* battAlertTypeStr(batt_alert_type_t type) {
|
||||
switch (type) {
|
||||
case BATT_ALERT_OVERVOLTAGE: return "overvoltage";
|
||||
case BATT_ALERT_CRITICAL_LOW: return "critical_low";
|
||||
case BATT_ALERT_LOW_VOLTAGE_WARNING: return "low_voltage_warning";
|
||||
case BATT_ALERT_SOC_LOW_20: return "low_20";
|
||||
case BATT_ALERT_SOC_LOW_10: return "low_10";
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
static bool postBatteryAlert(batt_alert_type_t type, uint8_t soc) {
|
||||
cJSON* payload = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(payload, "type", battAlertTypeStr(type));
|
||||
cJSON_AddNumberToObject(payload, "soc", soc);
|
||||
cJSON* response = nullptr;
|
||||
bool ok = httpPOST("battery_alert", webToken, payload, response);
|
||||
cJSON_Delete(payload);
|
||||
if (response) cJSON_Delete(response);
|
||||
return ok;
|
||||
}
|
||||
|
||||
static bool postBatterySoc(uint8_t soc) {
|
||||
cJSON* payload = cJSON_CreateObject();
|
||||
cJSON_AddNumberToObject(payload, "soc", soc);
|
||||
cJSON* response = nullptr;
|
||||
bool ok = httpPOST("battery_update", webToken, payload, response);
|
||||
cJSON_Delete(payload);
|
||||
if (response) cJSON_Delete(response);
|
||||
return ok;
|
||||
}
|
||||
|
||||
// ── Position helpers ──────────────────────────────────────────────────────────
|
||||
|
||||
bool postServoPos(uint8_t currentAppPos) {
|
||||
cJSON* posData = cJSON_CreateObject();
|
||||
cJSON_AddNumberToObject(posData, "port", 1);
|
||||
cJSON_AddNumberToObject(posData, "pos", currentAppPos);
|
||||
|
||||
cJSON* response = nullptr;
|
||||
bool success = httpPOST("position", webToken, posData, response);
|
||||
cJSON_Delete(posData);
|
||||
|
||||
if (success && response != nullptr) {
|
||||
cJSON* awaitCalibItem = cJSON_GetObjectItem(response, "await_calib");
|
||||
bool awaitCalib = cJSON_IsBool(awaitCalibItem) && awaitCalibItem->valueint != 0;
|
||||
printf("Position update sent: %d, await_calib=%d\n", currentAppPos, awaitCalib);
|
||||
cJSON_Delete(response);
|
||||
|
||||
if (awaitCalib) {
|
||||
gpio_set_level(debugLED, 1); // Start with LED off
|
||||
gpio_hold_en(debugLED);
|
||||
Calibration::clearCalibrated();
|
||||
if (!calibrate()) {
|
||||
if (!WiFi::attemptDHCPrenewal())
|
||||
setupAndCalibrate();
|
||||
else if (!calibrate()) {
|
||||
printf("ERROR: EVEN AFTER DHCP RENEWAL, SOCKET OPENING FAIL\n");
|
||||
setupAndCalibrate();
|
||||
}
|
||||
}
|
||||
gpio_hold_dis(debugLED);
|
||||
gpio_set_level(debugLED, 0);
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
bool getServoPos() {
|
||||
cJSON* response = nullptr;
|
||||
bool success = httpGET("position", webToken, response);
|
||||
|
||||
if (success && response != NULL) {
|
||||
if (cJSON_IsArray(response)) {
|
||||
int arraySize = cJSON_GetArraySize(response);
|
||||
|
||||
if (arraySize > 1) {
|
||||
printf("Multiple peripherals detected, entering setup.\n");
|
||||
cJSON_Delete(response);
|
||||
return false;
|
||||
} else if (arraySize > 0) {
|
||||
cJSON* firstObject = cJSON_GetArrayItem(response, 0);
|
||||
if (firstObject != NULL) {
|
||||
cJSON* peripheralNum = cJSON_GetObjectItem(firstObject, "peripheral_number");
|
||||
if (cJSON_IsNumber(peripheralNum) && peripheralNum->valueint != 1) {
|
||||
printf("Peripheral number is not 1, entering setup.\n");
|
||||
cJSON_Delete(response);
|
||||
return false;
|
||||
}
|
||||
printf("Verified new token!\n");
|
||||
|
||||
cJSON* awaitCalib = cJSON_GetObjectItem(firstObject, "await_calib");
|
||||
if (cJSON_IsBool(awaitCalib)) {
|
||||
if (awaitCalib->valueint) {
|
||||
Calibration::clearCalibrated();
|
||||
if (!calibrate()) {
|
||||
if (!WiFi::attemptDHCPrenewal())
|
||||
setupAndCalibrate();
|
||||
else if (!calibrate()) {
|
||||
printf("ERROR: EVEN AFTER DHCP RENEWAL, SOCKET OPENING FAIL\n");
|
||||
setupAndCalibrate();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
cJSON* pos = cJSON_GetObjectItem(firstObject, "last_pos");
|
||||
runToAppPos(pos->valueint);
|
||||
}
|
||||
}
|
||||
cJSON_Delete(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
// ── Wake timer (fires every 60 s) ────────────────────────────────────────────
|
||||
|
||||
void wakeTimer(void* pvParameters) {
|
||||
while (1) {
|
||||
vTaskDelay(pdMS_TO_TICKS(60000));
|
||||
if (setupTaskHandle != NULL || socketIOactive
|
||||
|| uxQueueMessagesWaiting(main_event_queue) > 2) continue;
|
||||
|
||||
main_event_type_t evt = EVENT_REQUEST_POS;
|
||||
xQueueSend(main_event_queue, &evt, portMAX_DELAY);
|
||||
|
||||
evt = EVENT_REPORT_SOC;
|
||||
xQueueSend(main_event_queue, &evt, portMAX_DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Main event loop ───────────────────────────────────────────────────────────
|
||||
|
||||
void mainEventLoop() {
|
||||
main_event_type_t received_event_type;
|
||||
|
||||
while (true) {
|
||||
if (!xQueueReceive(main_event_queue, &received_event_type, portMAX_DELAY)) continue;
|
||||
|
||||
if (received_event_type == EVENT_CLEAR_CALIB) {
|
||||
Calibration::clearCalibrated();
|
||||
if (!calibrate()) {
|
||||
if (!WiFi::attemptDHCPrenewal())
|
||||
setupAndCalibrate();
|
||||
else if (!calibrate()) {
|
||||
printf("ERROR: EVEN AFTER DHCP RENEWAL, SOCKET OPENING FAIL\n");
|
||||
setupAndCalibrate();
|
||||
}
|
||||
}
|
||||
|
||||
} else if (received_event_type == EVENT_SAVE_POS) {
|
||||
servoSavePos();
|
||||
uint8_t currentAppPos = Calibration::convertToAppPos(topEnc->getCount());
|
||||
if (!postServoPos(currentAppPos)) {
|
||||
printf("Failed to send position update\n");
|
||||
if (!WiFi::attemptDHCPrenewal()) {
|
||||
setupAndCalibrate();
|
||||
postServoPos(currentAppPos);
|
||||
} else if (!postServoPos(currentAppPos)) {
|
||||
printf("Renewed DHCP but still failed to post position\n");
|
||||
setupAndCalibrate();
|
||||
}
|
||||
}
|
||||
|
||||
} else if (received_event_type == EVENT_REQUEST_POS) {
|
||||
if (!getServoPos()) {
|
||||
printf("Failed to get position\n");
|
||||
if (!WiFi::attemptDHCPrenewal()) {
|
||||
setupAndCalibrate();
|
||||
getServoPos();
|
||||
} else if (!getServoPos()) {
|
||||
printf("Renewed DHCP but still failed to get position\n");
|
||||
setupAndCalibrate();
|
||||
}
|
||||
}
|
||||
|
||||
} else if (received_event_type == EVENT_BATTERY_CRITICAL) {
|
||||
// Stop the motor immediately, persist position, notify server, then hibernate.
|
||||
// esp_deep_sleep_start() with no wakeup source = indefinite sleep until reset.
|
||||
// The MAX17048 VRESET comparator handles detection of battery recovery.
|
||||
servoOff();
|
||||
servoSavePos();
|
||||
batt_alert_type_t alertType = bms_pending_alert; // snapshot volatile
|
||||
postBatteryAlert(alertType, established_soc);
|
||||
printf("CRITICAL BATTERY EVENT (%s, SOC=%d%%). Entering deep sleep.\n",
|
||||
battAlertTypeStr(alertType), established_soc);
|
||||
esp_deep_sleep_start();
|
||||
|
||||
} else if (received_event_type == EVENT_BATTERY_WARNING) {
|
||||
postBatteryAlert((batt_alert_type_t)bms_pending_alert, established_soc);
|
||||
|
||||
} else if (received_event_type == EVENT_REPORT_SOC) {
|
||||
postBatterySoc(established_soc);
|
||||
}
|
||||
}
|
||||
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
175
src/max17048.c
175
src/max17048.c
@@ -1,175 +0,0 @@
|
||||
#include "max17048.h"
|
||||
#include "mainEventLoop.hpp"
|
||||
#include "i2c.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
static const char *TAG = "BATTERY";
|
||||
|
||||
uint8_t established_soc = 100;
|
||||
volatile batt_alert_type_t bms_pending_alert = BATT_ALERT_CRITICAL_LOW;
|
||||
|
||||
static TaskHandle_t bms_event_handler = NULL;
|
||||
|
||||
void IRAM_ATTR alrt_ISR(void* arg) {
|
||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||
vTaskNotifyGiveFromISR(bms_event_handler, &xHigherPriorityTaskWoken);
|
||||
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
|
||||
}
|
||||
|
||||
esp_err_t max17048_init() {
|
||||
esp_err_t err = ESP_OK;
|
||||
err |= i2c_init();
|
||||
err |= bms_set_alert_bound_voltages(3.3f, 4.2f);
|
||||
err |= bms_set_reset_voltage(3.25f);
|
||||
err |= bms_set_alsc();
|
||||
|
||||
xTaskCreate(bms_checker_task, "BMS", 4096, NULL, 20, &bms_event_handler);
|
||||
|
||||
gpio_config_t io_conf = {
|
||||
.pin_bit_mask = (1ULL << maxALRT),
|
||||
.mode = GPIO_MODE_INPUT,
|
||||
.pull_up_en = GPIO_PULLUP_ENABLE,
|
||||
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
||||
.intr_type = GPIO_INTR_NEGEDGE,
|
||||
};
|
||||
|
||||
gpio_config(&io_conf);
|
||||
gpio_install_isr_service(ESP_INTR_FLAG_LEVEL1);
|
||||
gpio_isr_handler_add(maxALRT, alrt_ISR, NULL);
|
||||
|
||||
ESP_LOGI(TAG, "MAX17048 initialized, ALRT interrupt on GPIO %d", maxALRT);
|
||||
|
||||
err |= bms_clear_status();
|
||||
err |= bms_clear_alrt();
|
||||
return err;
|
||||
}
|
||||
|
||||
uint8_t bms_get_soc() {
|
||||
uint16_t raw_soc;
|
||||
if (max17048_read_reg(MAX17048_REG_SOC, ((uint8_t*)&raw_soc) + 1, (uint8_t*)&raw_soc) == ESP_OK) {
|
||||
// upper byte = whole percent; lower byte msb = 0.5%; round to nearest
|
||||
return (uint8_t)(raw_soc >> 8) + ((raw_soc & 0x80) ? 1 : 0);
|
||||
}
|
||||
ESP_LOGE(TAG, "Failed to read SOC register");
|
||||
return 0;
|
||||
}
|
||||
|
||||
esp_err_t bms_set_alert_bound_voltages(float min, float max) {
|
||||
uint8_t minVal = (uint8_t)((uint16_t)(min * 1000.0f) / 20);
|
||||
uint8_t maxVal = (uint8_t)((uint16_t)(max * 1000.0f) / 20);
|
||||
return max17048_write_reg(MAX17048_REG_VALRT, minVal, maxVal);
|
||||
}
|
||||
|
||||
esp_err_t bms_set_reset_voltage(float vreset) {
|
||||
uint8_t val = (uint8_t)((uint16_t)(vreset * 1000.0f) / 40);
|
||||
return max17048_write_reg(MAX17048_REG_VRST_ID, val, 0);
|
||||
}
|
||||
|
||||
void bms_checker_task(void *pvParameters) {
|
||||
uint8_t prev_soc = 100;
|
||||
|
||||
while (true) {
|
||||
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
||||
|
||||
uint8_t status, _;
|
||||
if (max17048_read_reg(MAX17048_REG_STATUS, &status, &_) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "STATUS read failed");
|
||||
bms_clear_alrt();
|
||||
continue;
|
||||
}
|
||||
|
||||
uint8_t soc = bms_get_soc();
|
||||
established_soc = soc;
|
||||
|
||||
// Clear before posting to queue so ALRT pin is deasserted promptly
|
||||
bms_clear_status();
|
||||
bms_clear_alrt();
|
||||
|
||||
main_event_type_t evt;
|
||||
|
||||
if ((status & HDbit) || ((status & VLbit) && soc < SOC_CRITICAL_VL)) {
|
||||
// Critical: overvoltage (hardware fault) or battery truly empty
|
||||
bms_pending_alert = (status & VHbit) ? BATT_ALERT_OVERVOLTAGE : BATT_ALERT_CRITICAL_LOW;
|
||||
evt = EVENT_BATTERY_CRITICAL;
|
||||
xQueueSend(main_event_queue, &evt, portMAX_DELAY);
|
||||
|
||||
} else if (status & VLbit) {
|
||||
// Undervoltage but SOC still healthy — likely a transient load spike
|
||||
bms_pending_alert = BATT_ALERT_LOW_VOLTAGE_WARNING;
|
||||
evt = EVENT_BATTERY_WARNING;
|
||||
xQueueSend(main_event_queue, &evt, portMAX_DELAY);
|
||||
|
||||
} else if (status & SCbit) {
|
||||
// 1% SOC change: check downward threshold crossings for user notifications
|
||||
if (soc <= SOC_WARN_10 && prev_soc > SOC_WARN_10) {
|
||||
bms_pending_alert = BATT_ALERT_SOC_LOW_10;
|
||||
evt = EVENT_BATTERY_WARNING;
|
||||
xQueueSend(main_event_queue, &evt, portMAX_DELAY);
|
||||
} else if (soc <= SOC_WARN_20 && prev_soc > SOC_WARN_20) {
|
||||
bms_pending_alert = BATT_ALERT_SOC_LOW_20;
|
||||
evt = EVENT_BATTERY_WARNING;
|
||||
xQueueSend(main_event_queue, &evt, portMAX_DELAY);
|
||||
}
|
||||
prev_soc = soc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Helper: Read 16-bit register to 2-byte array (MSB first big endian)
|
||||
esp_err_t max17048_read_reg(uint8_t reg_addr, uint8_t *MSB, uint8_t *LSB) {
|
||||
// this is better than converting to little endian for my application
|
||||
// since I usually need to handle bytes individually.
|
||||
uint8_t data[2];
|
||||
// Write register address
|
||||
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||
i2c_master_start(cmd);
|
||||
i2c_master_write_byte(cmd, (MAX17048_ADDR << 1) | I2C_MASTER_WRITE, true);
|
||||
i2c_master_write_byte(cmd, reg_addr, true);
|
||||
|
||||
// Restart and Read 2 bytes
|
||||
i2c_master_start(cmd);
|
||||
i2c_master_write_byte(cmd, (MAX17048_ADDR << 1) | I2C_MASTER_READ, true);
|
||||
i2c_master_read(cmd, data, 2, I2C_MASTER_LAST_NACK);
|
||||
i2c_master_stop(cmd);
|
||||
|
||||
esp_err_t ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, pdMS_TO_TICKS(I2C_MASTER_TIMEOUT_MS));
|
||||
i2c_cmd_link_delete(cmd);
|
||||
|
||||
if (ret == ESP_OK) {
|
||||
*MSB = data[0];
|
||||
*LSB = data[1];
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Write big endian 2-byte array to a 16-bit register
|
||||
esp_err_t max17048_write_reg(uint8_t reg_addr, uint8_t MSB, uint8_t LSB) {
|
||||
// Write register address
|
||||
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||
i2c_master_start(cmd);
|
||||
i2c_master_write_byte(cmd, (MAX17048_ADDR << 1) | I2C_MASTER_WRITE, true);
|
||||
i2c_master_write_byte(cmd, reg_addr, true);
|
||||
i2c_master_write_byte(cmd, MSB, true);
|
||||
i2c_master_write_byte(cmd, LSB, true);
|
||||
i2c_master_stop(cmd);
|
||||
|
||||
esp_err_t ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, pdMS_TO_TICKS(I2C_MASTER_TIMEOUT_MS));
|
||||
i2c_cmd_link_delete(cmd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t max17048_friendly_write_reg(uint8_t reg_addr, uint8_t MSB, uint8_t LSB,
|
||||
uint8_t MSBmask, uint8_t LSBmask) {
|
||||
uint8_t origMSB, origLSB;
|
||||
esp_err_t err = max17048_read_reg(reg_addr, &origMSB, &origLSB);
|
||||
MSB &= MSBmask;
|
||||
LSB &= LSBmask;
|
||||
MSB |= origMSB & ~MSBmask;
|
||||
LSB |= origLSB & ~LSBmask;
|
||||
return err | max17048_write_reg(reg_addr, MSB, LSB);
|
||||
}
|
||||
@@ -1,12 +1,10 @@
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "servo.hpp"
|
||||
#include "driver/ledc.h"
|
||||
#include "defines.h"
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include "esp_log.h"
|
||||
#include "socketIO.hpp"
|
||||
#include "nvs_flash.h"
|
||||
#include "mainEventLoop.hpp"
|
||||
|
||||
std::atomic<bool> calibListen{false};
|
||||
std::atomic<int32_t> baseDiff{0};
|
||||
@@ -14,6 +12,8 @@ std::atomic<int32_t> target{0};
|
||||
|
||||
std::atomic<bool> runningManual{false};
|
||||
std::atomic<bool> runningServer{false};
|
||||
std::atomic<bool> clearCalibFlag{false};
|
||||
std::atomic<bool> savePosFlag{false};
|
||||
std::atomic<bool> startLess{false};
|
||||
|
||||
void servoInit() {
|
||||
@@ -23,7 +23,7 @@ void servoInit() {
|
||||
ledc_timer.timer_num = LEDC_TIMER_0;
|
||||
ledc_timer.duty_resolution = LEDC_TIMER_16_BIT;
|
||||
ledc_timer.freq_hz = 50;
|
||||
ledc_timer.clk_cfg = LEDC_USE_RC_FAST_CLK;
|
||||
ledc_timer.clk_cfg = LEDC_AUTO_CLK;
|
||||
ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer));
|
||||
|
||||
// LEDC channel configuration
|
||||
@@ -35,9 +35,7 @@ void servoInit() {
|
||||
ledc_channel.gpio_num = servoPin;
|
||||
ledc_channel.duty = offSpeed; // Start off
|
||||
ledc_channel.hpoint = 0;
|
||||
ledc_channel.sleep_mode = LEDC_SLEEP_MODE_KEEP_ALIVE;
|
||||
ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel));
|
||||
gpio_sleep_sel_dis(servoPin);
|
||||
|
||||
// Configure servo power switch pin as output
|
||||
gpio_reset_pin(servoSwitch);
|
||||
@@ -45,14 +43,13 @@ void servoInit() {
|
||||
gpio_set_level(servoSwitch, 0); // Start with servo power off
|
||||
|
||||
// Configure debug LED pin as output
|
||||
gpio_reset_pin(debugLED);
|
||||
gpio_reset_pin(GPIO_NUM_22);
|
||||
gpio_set_direction(debugLED, GPIO_MODE_OUTPUT);
|
||||
gpio_set_level(debugLED, 0); // Start with LED off
|
||||
|
||||
topEnc->count = servoReadPos();
|
||||
bottomEnc->count = servoReadPos();
|
||||
if (Calibration::getCalibrated()) initMainLoop();
|
||||
// debugLEDSwitch(1);
|
||||
if (calib.getCalibrated()) initMainLoop();
|
||||
debugLEDSwitch(1);
|
||||
}
|
||||
|
||||
void servoOn(uint8_t dir, uint8_t manOrServer) {
|
||||
@@ -91,7 +88,7 @@ bool servoInitCalib() {
|
||||
bottomEnc->wandListen.store(false, std::memory_order_release);
|
||||
topEnc->wandListen.store(false, std::memory_order_release);
|
||||
topEnc->serverListen.store(false, std::memory_order_release);
|
||||
if (!Calibration::clearCalibrated()) return false;
|
||||
if (!calib.clearCalibrated()) return false;
|
||||
if (topEnc == nullptr || bottomEnc == nullptr) {
|
||||
printf("ERROR: CALIBRATION STARTED BEFORE SERVO INITIALIZATION\n");
|
||||
return false;
|
||||
@@ -121,7 +118,7 @@ bool servoBeginDownwardCalib() {
|
||||
calibListen = false;
|
||||
servoOff();
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
if (!Calibration::beginDownwardCalib(*topEnc)) return false;
|
||||
if (!calib.beginDownwardCalib(*topEnc)) return false;
|
||||
baseDiff = bottomEnc->getCount() - topEnc->getCount();
|
||||
calibListen = true;
|
||||
return true;
|
||||
@@ -131,7 +128,7 @@ bool servoCompleteCalib() {
|
||||
calibListen = false;
|
||||
servoOff();
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
if (!Calibration::completeCalib(*topEnc)) return false;
|
||||
if (!calib.completeCalib(*topEnc)) return false;
|
||||
initMainLoop();
|
||||
return true;
|
||||
}
|
||||
@@ -144,6 +141,8 @@ void initMainLoop() {
|
||||
|
||||
void IRAM_ATTR watchdogCallback(void* arg) {
|
||||
if (runningManual || runningServer) {
|
||||
// if we're trying to move and our timer ran out, we need to recalibrate
|
||||
clearCalibFlag = true;
|
||||
topEnc->pauseWatchdog();
|
||||
|
||||
// get ready for recalibration by clearing all these listeners
|
||||
@@ -151,18 +150,11 @@ void IRAM_ATTR watchdogCallback(void* arg) {
|
||||
topEnc->wandListen.store(false, std::memory_order_release);
|
||||
topEnc->serverListen.store(false, std::memory_order_release);
|
||||
servoOff();
|
||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||
main_event_type_t evt = EVENT_CLEAR_CALIB;
|
||||
BaseType_t result = xQueueSendFromISR(main_event_queue, &evt, &xHigherPriorityTaskWoken);
|
||||
if (result == pdPASS) portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
|
||||
}
|
||||
else {
|
||||
// if no movement is running, we're fine
|
||||
// save current servo-encoder position for reinitialization
|
||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||
main_event_type_t evt = EVENT_SAVE_POS;
|
||||
BaseType_t result = xQueueSendFromISR(main_event_queue, &evt, &xHigherPriorityTaskWoken);
|
||||
if (result == pdPASS) portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
|
||||
savePosFlag = true;
|
||||
}
|
||||
// clear running flags
|
||||
runningManual = false;
|
||||
@@ -212,8 +204,8 @@ void servoWandListen() {
|
||||
stopServerRun();
|
||||
|
||||
// freeze atomic values
|
||||
int32_t upBound = Calibration::UpTicks;
|
||||
int32_t downBound = Calibration::DownTicks;
|
||||
int32_t upBound = calib.UpTicks;
|
||||
int32_t downBound = calib.DownTicks;
|
||||
int32_t bottomCount = bottomEnc->getCount();
|
||||
int32_t topCount = topEnc->getCount();
|
||||
|
||||
@@ -233,20 +225,20 @@ void servoWandListen() {
|
||||
// otherwise, run servo in whichever direction necessary and
|
||||
// ensure servo-listener is active.
|
||||
if (topCount >= (MAX(upBound, downBound) - 1)
|
||||
&& effDiff > 2) { // TODO: see whether these margins need to be removed.
|
||||
&& effDiff > 1) { // TODO: see whether these margins need to be removed.
|
||||
servoOff();
|
||||
topEnc->wandListen.store(false, std::memory_order_release);
|
||||
}
|
||||
else if (topCount <= (MIN(upBound, downBound) + 1)
|
||||
&& effDiff < -2) {
|
||||
&& effDiff < -1) {
|
||||
servoOff();
|
||||
topEnc->wandListen.store(false, std::memory_order_release);
|
||||
}
|
||||
else if (effDiff > 2) {
|
||||
else if (effDiff > 1) {
|
||||
topEnc->wandListen.store(true, std::memory_order_release);
|
||||
servoOn(CCW, manual);
|
||||
}
|
||||
else if (effDiff < -2) {
|
||||
else if (effDiff < -1) {
|
||||
topEnc->wandListen.store(true, std::memory_order_release);
|
||||
servoOn(CW, manual);
|
||||
}
|
||||
@@ -266,15 +258,11 @@ void servoServerListen() {
|
||||
void runToAppPos(uint8_t appPos) {
|
||||
// manual control takes precedence over remote control, always.
|
||||
// also do not begin operation if not calibrated;
|
||||
if (runningManual || !Calibration::getCalibrated()) return;
|
||||
if (runningManual || !calib.getCalibrated()) return;
|
||||
servoOff();
|
||||
|
||||
if (Calibration::convertToAppPos(topEnc->getCount()) == appPos) {
|
||||
printf("Already at pos: %d, not running\n", appPos);
|
||||
return;
|
||||
}
|
||||
target = Calibration::convertToTicks(appPos); // calculate target encoder position
|
||||
printf("runToAppPos Called, running to %d from %d\n", target.load(), topEnc->getCount());
|
||||
target = calib.convertToTicks(appPos); // calculate target encoder position
|
||||
printf("runToAppPos Called, running to %d from %d", target.load(), topEnc->getCount());
|
||||
|
||||
// allow servo position to settle
|
||||
vTaskDelay(pdMS_TO_TICKS(500));
|
||||
|
||||
127
src/setup.cpp
127
src/setup.cpp
@@ -1,4 +1,3 @@
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "setup.hpp"
|
||||
#include "BLE.hpp"
|
||||
#include "WiFi.hpp"
|
||||
@@ -6,37 +5,17 @@
|
||||
#include "defines.h"
|
||||
#include "bmHTTP.hpp"
|
||||
#include "socketIO.hpp"
|
||||
#include "calibration.hpp"
|
||||
|
||||
TaskHandle_t setupTaskHandle = NULL;
|
||||
|
||||
std::atomic<bool> awaitCalibration{false};
|
||||
|
||||
void initialSetup() {
|
||||
printf("Entered Setup\n");
|
||||
initBLE();
|
||||
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
||||
setupTaskHandle = NULL;
|
||||
}
|
||||
NimBLEAdvertising* pAdv = initBLE();
|
||||
|
||||
void setupAndCalibrate() {
|
||||
gpio_reset_pin(debugLED);
|
||||
gpio_set_direction(debugLED, GPIO_MODE_OUTPUT);
|
||||
gpio_set_level(debugLED, 1); // Start with LED off
|
||||
gpio_hold_en(debugLED);
|
||||
while (1) {
|
||||
setupLoop();
|
||||
if (awaitCalibration) {
|
||||
if (calibrate()) break;
|
||||
while (!BLEtick(pAdv)) {
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
}
|
||||
else break;
|
||||
}
|
||||
gpio_hold_dis(debugLED);
|
||||
gpio_set_level(debugLED, 0); // Start with LED off
|
||||
}
|
||||
|
||||
void setupLoop() {
|
||||
setupTaskHandle = xTaskGetCurrentTaskHandle();
|
||||
bool initSuccess = false;
|
||||
while(!initSuccess) {
|
||||
nvs_handle_t WiFiHandle;
|
||||
@@ -52,19 +31,16 @@ void setupLoop() {
|
||||
// Make the RGB LED a certain color (Blue?)
|
||||
nvs_close(WiFiHandle);
|
||||
initialSetup();
|
||||
continue;
|
||||
}
|
||||
else if (WiFiPrefsError == ESP_OK) {
|
||||
} else if (WiFiPrefsError == ESP_OK) {
|
||||
char ssid[ssidSize];
|
||||
nvs_get_str(WiFiHandle, ssidTag, ssid, &ssidSize);
|
||||
char pw[pwSize];
|
||||
nvs_get_str(WiFiHandle, passTag, pw, &pwSize);
|
||||
nvs_close(WiFiHandle);
|
||||
if (!WiFi::attemptConnect(ssid, pw, (wifi_auth_mode_t)authMode)) {
|
||||
if (!bmWiFi.attemptConnect(ssid, pw, (wifi_auth_mode_t)authMode)) {
|
||||
// Make RGB LED certain color (Blue?)
|
||||
printf("Found credentials, failed to connect.\n");
|
||||
initialSetup();
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
printf("Connected to WiFi from NVS credentials\n");
|
||||
@@ -79,90 +55,34 @@ void setupLoop() {
|
||||
// Use permanent device token to connect to Socket.IO
|
||||
// The server will verify the token during connection handshake
|
||||
webToken = std::string(token);
|
||||
cJSON* response = nullptr;
|
||||
initSuccess = httpGET("position", webToken, response);
|
||||
if (!initSuccess) {
|
||||
initialSetup();
|
||||
continue;
|
||||
printf("Connecting to Socket.IO server with saved token...\n");
|
||||
statusResolved = false;
|
||||
initSocketIO();
|
||||
|
||||
// Wait for device_init message from server with timeout
|
||||
int timeout_count = 0;
|
||||
const int MAX_TIMEOUT = 60; // 10 seconds (20 * 500ms)
|
||||
while (!statusResolved && timeout_count < MAX_TIMEOUT) {
|
||||
printf("Waiting for device_init message... (%d/%d)\n", timeout_count, MAX_TIMEOUT);
|
||||
vTaskDelay(pdMS_TO_TICKS(500));
|
||||
timeout_count++;
|
||||
}
|
||||
|
||||
if (timeout_count >= MAX_TIMEOUT) {
|
||||
printf("Timeout waiting for device_init - connection failed\n");
|
||||
stopSocketIO();
|
||||
initSuccess = false;
|
||||
|
||||
if (response != NULL) {
|
||||
// Check if response is an array
|
||||
if (cJSON_IsArray(response)) {
|
||||
int arraySize = cJSON_GetArraySize(response);
|
||||
|
||||
// Condition 1: More than one object in array
|
||||
if (arraySize > 1) {
|
||||
printf("Multiple peripherals detected, entering setup.\n");
|
||||
cJSON_Delete(response);
|
||||
initialSetup();
|
||||
continue;
|
||||
}
|
||||
// Condition 2: Check peripheral_number in first object
|
||||
else if (arraySize > 0) {
|
||||
cJSON *firstObject = cJSON_GetArrayItem(response, 0);
|
||||
if (firstObject != NULL) {
|
||||
cJSON *peripheralNum = cJSON_GetObjectItem(firstObject, "peripheral_number");
|
||||
if (cJSON_IsNumber(peripheralNum) && peripheralNum->valueint != 1) {
|
||||
printf("Peripheral number is not 1, entering setup.\n");
|
||||
cJSON_Delete(response);
|
||||
initialSetup();
|
||||
continue;
|
||||
}
|
||||
// Valid single peripheral with number 1, continue with normal flow
|
||||
initSuccess = true;
|
||||
printf("Verified new token!\n");
|
||||
|
||||
cJSON *awaitCalib = cJSON_GetObjectItem(firstObject, "await_calib");
|
||||
if (cJSON_IsBool(awaitCalib)) awaitCalibration = awaitCalib->valueint;
|
||||
cJSON_Delete(response);
|
||||
if (!awaitCalibration) {
|
||||
// Create calibration status object
|
||||
cJSON* calibPostObj = cJSON_CreateObject();
|
||||
cJSON_AddNumberToObject(calibPostObj, "port", 1);
|
||||
cJSON_AddBoolToObject(calibPostObj, "calibrated", Calibration::getCalibrated());
|
||||
|
||||
// Send calibration status to server
|
||||
cJSON* calibResponse = nullptr;
|
||||
bool calibSuccess = httpPOST("report_calib_status", webToken, calibPostObj, calibResponse);
|
||||
|
||||
if (calibSuccess && calibResponse != NULL) {
|
||||
printf("Calibration status reported successfully\n");
|
||||
cJSON_Delete(calibResponse);
|
||||
} else {
|
||||
printf("Failed to report calibration status\n");
|
||||
}
|
||||
cJSON_Delete(calibPostObj);
|
||||
if (!Calibration::getCalibrated()) awaitCalibration = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
printf("null object\n");
|
||||
cJSON_Delete(response);
|
||||
initSuccess = connected;
|
||||
if (!initSuccess) {
|
||||
printf("Device authentication failed - entering setup\n");
|
||||
initialSetup();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
printf("no items in array\n");
|
||||
cJSON_Delete(response);
|
||||
initialSetup();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
printf("Response not array\n");
|
||||
cJSON_Delete(response);
|
||||
initialSetup();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else printf("Failed to parse JSON response\n");
|
||||
}
|
||||
else {
|
||||
printf("Token read unsuccessful, entering setup.\n");
|
||||
nvs_close(authHandle);
|
||||
initialSetup();
|
||||
}
|
||||
}
|
||||
@@ -183,5 +103,4 @@ void setupLoop() {
|
||||
initialSetup();
|
||||
}
|
||||
}
|
||||
setupTaskHandle = NULL; // Clear handle on function exit (safety)
|
||||
}
|
||||
@@ -11,8 +11,9 @@
|
||||
|
||||
static esp_socketio_client_handle_t io_client;
|
||||
static esp_socketio_packet_handle_t tx_packet = NULL;
|
||||
static bool stopSocketFlag = false;
|
||||
std::atomic<bool> socketIOactive{false};
|
||||
|
||||
std::atomic<bool> statusResolved{true};
|
||||
std::atomic<bool> connected{false};
|
||||
|
||||
// Event handler for Socket.IO events
|
||||
static void socketio_event_handler(void *handler_args, esp_event_base_t base,
|
||||
@@ -64,8 +65,8 @@ static void socketio_event_handler(void *handler_args, esp_event_base_t base,
|
||||
}
|
||||
|
||||
// Mark connection as failed
|
||||
if (calibTaskHandle != NULL)
|
||||
xTaskNotify(calibTaskHandle, false, eSetValueWithOverwrite);
|
||||
connected = false;
|
||||
statusResolved = true;
|
||||
}
|
||||
// Handle device_init event
|
||||
else if (strcmp(eventName->valuestring, "device_init") == 0) {
|
||||
@@ -90,18 +91,25 @@ static void socketio_event_handler(void *handler_args, esp_event_base_t base,
|
||||
// TODO: UPDATE MOTOR/ENCODER STATES BASED ON THIS, as well as the successive websocket updates.
|
||||
printf(" Port %d: pos=%d\n", port, lastPos);
|
||||
if (port != 1) printf("ERROR: NON-1 PORT RECEIVED\n");
|
||||
// Report back actual calibration status from device
|
||||
else {
|
||||
bool deviceCalibrated = calib.getCalibrated();
|
||||
emitCalibStatus(deviceCalibrated);
|
||||
printf(" Reported calibrated=%d for port %d\n", deviceCalibrated, port);
|
||||
runToAppPos(lastPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now mark as connected
|
||||
if (calibTaskHandle != NULL)
|
||||
xTaskNotify(calibTaskHandle, true, eSetValueWithOverwrite);
|
||||
connected = true;
|
||||
statusResolved = true;
|
||||
} else {
|
||||
printf("Device authentication failed\n");
|
||||
Calibration::clearCalibrated();
|
||||
calib.clearCalibrated();
|
||||
deleteWiFiAndTokenDetails();
|
||||
if (calibTaskHandle != NULL)
|
||||
xTaskNotify(calibTaskHandle, false, eSetValueWithOverwrite);
|
||||
connected = false;
|
||||
statusResolved = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -115,10 +123,10 @@ static void socketio_event_handler(void *handler_args, esp_event_base_t base,
|
||||
printf("Server message: %s\n", message->valuestring);
|
||||
}
|
||||
}
|
||||
Calibration::clearCalibrated();
|
||||
calib.clearCalibrated();
|
||||
deleteWiFiAndTokenDetails();
|
||||
if (calibTaskHandle != NULL)
|
||||
xTaskNotify(calibTaskHandle, false, eSetValueWithOverwrite);
|
||||
connected = false;
|
||||
statusResolved = true;
|
||||
}
|
||||
|
||||
// Handle calib_start event
|
||||
@@ -180,21 +188,13 @@ static void socketio_event_handler(void *handler_args, esp_event_base_t base,
|
||||
}
|
||||
else {
|
||||
if (!servoCompleteCalib()) emitCalibError("Completion failed");
|
||||
else {
|
||||
emitCalibDone();
|
||||
}
|
||||
else emitCalibDone();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle calib_done_ack event
|
||||
else if (strcmp(eventName->valuestring, "calib_done_ack") == 0) {
|
||||
printf("Server acknowledged calibration completion - safe to disconnect\n");
|
||||
stopSocketFlag = true;
|
||||
}
|
||||
|
||||
// Handle cancel_calib event
|
||||
// Handle user_stage1_complete event
|
||||
else if (strcmp(eventName->valuestring, "cancel_calib") == 0) {
|
||||
printf("Canceling calibration process...\n");
|
||||
cJSON *data = cJSON_GetArrayItem(json, 1);
|
||||
@@ -211,6 +211,37 @@ static void socketio_event_handler(void *handler_args, esp_event_base_t base,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle server position change (manual or scheduled)
|
||||
else if (strcmp(eventName->valuestring, "posUpdates") == 0) {
|
||||
printf("Received position update from server\n");
|
||||
cJSON *updateList = cJSON_GetArrayItem(json, 1);
|
||||
|
||||
if (cJSON_IsArray(updateList)) {
|
||||
int updateCount = cJSON_GetArraySize(updateList);
|
||||
printf("Processing %d position update(s)\n", updateCount);
|
||||
|
||||
for (int i = 0; i < updateCount; i++) {
|
||||
cJSON *update = cJSON_GetArrayItem(updateList, i);
|
||||
cJSON *periphNum = cJSON_GetObjectItem(update, "periphNum");
|
||||
cJSON *pos = cJSON_GetObjectItem(update, "pos");
|
||||
|
||||
if (periphNum && cJSON_IsNumber(periphNum) &&
|
||||
pos && cJSON_IsNumber(pos)) {
|
||||
int port = periphNum->valueint;
|
||||
int position = pos->valueint;
|
||||
|
||||
if (port != 1)
|
||||
printf("ERROR: Received position update for non-1 port: %d\n", port);
|
||||
else {
|
||||
printf("Position update: position %d\n", position);
|
||||
runToAppPos(position);
|
||||
}
|
||||
}
|
||||
else printf("Invalid position update format\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -262,8 +293,8 @@ static void socketio_event_handler(void *handler_args, esp_event_base_t base,
|
||||
}
|
||||
}
|
||||
|
||||
if (calibTaskHandle != NULL)
|
||||
xTaskNotify(calibTaskHandle, false, eSetValueWithOverwrite);
|
||||
connected = false;
|
||||
statusResolved = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -271,22 +302,19 @@ static void socketio_event_handler(void *handler_args, esp_event_base_t base,
|
||||
// Handle WebSocket-level disconnections
|
||||
if (data->websocket_event_id == WEBSOCKET_EVENT_DISCONNECTED) {
|
||||
printf("WebSocket disconnected\n");
|
||||
if (calibTaskHandle != NULL)
|
||||
xTaskNotify(calibTaskHandle, false, eSetValueWithOverwrite);
|
||||
}
|
||||
if (stopSocketFlag) {
|
||||
if (calibTaskHandle != NULL)
|
||||
xTaskNotify(calibTaskHandle, 2, eSetValueWithOverwrite);
|
||||
stopSocketFlag = false; // Clear flag after notifying once
|
||||
connected = false;
|
||||
statusResolved = true;
|
||||
}
|
||||
}
|
||||
|
||||
const std::string uriString = std::string("ws") + (secureSrv ? "s" : "") + "://" + srvAddr + "/socket.io/?EIO=4&transport=websocket";
|
||||
void initSocketIO() {
|
||||
stopSocketFlag = false; // Reset flag for new connection
|
||||
// Prepare the Authorization Header (Bearer format)
|
||||
std::string authHeader = "Authorization: Bearer " + webToken + "\r\n";
|
||||
|
||||
statusResolved = false;
|
||||
connected = false;
|
||||
|
||||
esp_socketio_client_config_t config = {};
|
||||
config.websocket_config.uri = uriString.c_str();
|
||||
config.websocket_config.headers = authHeader.c_str();
|
||||
@@ -301,8 +329,6 @@ void initSocketIO() {
|
||||
|
||||
esp_socketio_register_events(io_client, SOCKETIO_EVENT_ANY, socketio_event_handler, NULL);
|
||||
esp_socketio_client_start(io_client);
|
||||
|
||||
socketIOactive = true;
|
||||
}
|
||||
|
||||
void stopSocketIO() {
|
||||
@@ -312,8 +338,9 @@ void stopSocketIO() {
|
||||
esp_socketio_client_destroy(io_client);
|
||||
io_client = NULL;
|
||||
tx_packet = NULL;
|
||||
connected = false;
|
||||
statusResolved = false;
|
||||
}
|
||||
socketIOactive = false;
|
||||
}
|
||||
|
||||
// Helper function to emit Socket.IO event with data
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
#include "bms_test.hpp"
|
||||
#include "i2c.h"
|
||||
#include "max17048.h"
|
||||
#include "defines.h"
|
||||
|
||||
void bms_test_LED() {
|
||||
vTaskDelay(pdMS_TO_TICKS(5000));
|
||||
i2c_init();
|
||||
|
||||
gpio_config_t led_conf = {
|
||||
.pin_bit_mask = (1ULL << debugLED),
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
.pull_up_en = GPIO_PULLUP_DISABLE,
|
||||
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
||||
.intr_type = GPIO_INTR_DISABLE,
|
||||
};
|
||||
gpio_config(&led_conf);
|
||||
|
||||
bms_set_alert_bound_voltages(3.8f, 3.9f);
|
||||
bms_clear_status();
|
||||
bms_clear_alrt();
|
||||
|
||||
while (true) {
|
||||
uint8_t msb, lsb;
|
||||
if (max17048_read_reg(MAX17048_REG_STATUS, &msb, &lsb) == ESP_OK) {
|
||||
bool vh = !!(msb & VHbit);
|
||||
bool vl = !!(msb & VLbit);
|
||||
|
||||
bms_clear_status();
|
||||
bms_clear_alrt();
|
||||
if (vl) {
|
||||
// Under-voltage: LED off
|
||||
gpio_set_level(debugLED, 0);
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
} else if (vh) {
|
||||
// Over-voltage: rapid blink
|
||||
gpio_set_level(debugLED, 1);
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
gpio_set_level(debugLED, 0);
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
} else {
|
||||
// Normal: LED on
|
||||
gpio_set_level(debugLED, 1);
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
}
|
||||
} else {
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bms_test() {
|
||||
vTaskDelay(pdMS_TO_TICKS(5000));
|
||||
esp_err_t err = i2c_init();
|
||||
|
||||
printf("Error after init: %d\n", err);
|
||||
|
||||
gpio_config_t io_conf = {
|
||||
.pin_bit_mask = (1ULL << maxALRT),
|
||||
.mode = GPIO_MODE_INPUT,
|
||||
.pull_up_en = GPIO_PULLUP_ENABLE,
|
||||
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
||||
.intr_type = GPIO_INTR_DISABLE,
|
||||
};
|
||||
gpio_config(&io_conf);
|
||||
|
||||
printf("BMS test running. Polling ALRT pin every 1s...\n");
|
||||
|
||||
|
||||
uint8_t msb, lsb;
|
||||
if (max17048_read_reg(MAX17048_REG_STATUS, &msb, &lsb) == ESP_OK) {
|
||||
printf(" STATUS before clear: RI=%d\n", !!(msb & RIbit));
|
||||
printf(" STATUS reg before clear: RI=%x, %x\n", msb, lsb);
|
||||
} else {
|
||||
printf(" STATUS read FAILED\n");
|
||||
}
|
||||
|
||||
err = bms_set_alert_bound_voltages(3.3f, 4.2f);
|
||||
printf("Error after boundV: %d\n", err);
|
||||
err |= bms_clear_status();
|
||||
printf("Error after clearStatus: %d\n", err);
|
||||
err |= bms_clear_alrt();
|
||||
printf("Error after clearALRT: %d\n", err);
|
||||
|
||||
if (max17048_read_reg(MAX17048_REG_STATUS, &msb, &lsb) == ESP_OK) {
|
||||
printf(" STATUS after clear: RI=%d\n", !!(msb & RIbit));
|
||||
printf(" STATUS reg after clear: RI=%x, %x\n", msb, lsb);
|
||||
} else {
|
||||
printf(" STATUS read FAILED\n");
|
||||
}
|
||||
|
||||
while (true) {
|
||||
int level = gpio_get_level(maxALRT);
|
||||
printf("ALRT pin: %s\n", level ? "HIGH (no alert)" : "LOW (alert asserted)");
|
||||
|
||||
if (level == 0) {
|
||||
if (max17048_read_reg(MAX17048_REG_STATUS, &msb, &lsb) == ESP_OK) {
|
||||
printf(" STATUS: VH=%d VL=%d HD=%d SC=%d\n",
|
||||
!!(msb & VHbit),
|
||||
!!(msb & VLbit),
|
||||
!!(msb & HDbit),
|
||||
!!(msb & SCbit));
|
||||
bms_clear_status();
|
||||
bms_clear_alrt();
|
||||
} else {
|
||||
printf(" STATUS read FAILED\n");
|
||||
}
|
||||
}
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
#ifndef BMS_TEST_H
|
||||
#define BMS_TEST_H
|
||||
|
||||
void bms_test();
|
||||
void bms_test_LED();
|
||||
|
||||
#endif
|
||||
@@ -1,104 +0,0 @@
|
||||
#include "encoder_test.hpp"
|
||||
#include "defines.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "soc/gpio_struct.h"
|
||||
|
||||
typedef struct {
|
||||
uint8_t encoder_id; // 0 = top, 1 = bottom
|
||||
int8_t direction; // +1 or -1
|
||||
} enc_event_t;
|
||||
|
||||
typedef struct {
|
||||
gpio_num_t pin_a;
|
||||
gpio_num_t pin_b;
|
||||
uint8_t id;
|
||||
uint8_t last_a;
|
||||
uint8_t last_b;
|
||||
int8_t base;
|
||||
} enc_state_t;
|
||||
|
||||
static QueueHandle_t enc_queue;
|
||||
static enc_state_t enc_states[2];
|
||||
static bool led_state = false;
|
||||
|
||||
static void IRAM_ATTR enc_isr(void* arg) {
|
||||
enc_state_t* s = (enc_state_t*)arg;
|
||||
|
||||
uint32_t levels = GPIO.in.val;
|
||||
uint8_t a = (levels >> s->pin_a) & 1;
|
||||
uint8_t b = (levels >> s->pin_b) & 1;
|
||||
|
||||
if (a != s->last_a) {
|
||||
if (!a) s->base += b ? 1 : -1;
|
||||
else s->base += b ? -1 : 1;
|
||||
} else if (b != s->last_b) {
|
||||
if (!b) s->base += a ? -1 : 1;
|
||||
else s->base += a ? 1 : -1;
|
||||
}
|
||||
|
||||
s->last_a = a;
|
||||
s->last_b = b;
|
||||
|
||||
enc_event_t evt;
|
||||
evt.encoder_id = s->id;
|
||||
BaseType_t woken = pdFALSE;
|
||||
|
||||
if (s->base >= 4) {
|
||||
s->base -= 4;
|
||||
evt.direction = 1;
|
||||
led_state = !led_state;
|
||||
gpio_set_level(debugLED, led_state);
|
||||
xQueueSendFromISR(enc_queue, &evt, &woken);
|
||||
portYIELD_FROM_ISR(woken);
|
||||
} else if (s->base <= -4) {
|
||||
s->base += 4;
|
||||
evt.direction = -1;
|
||||
led_state = !led_state;
|
||||
gpio_set_level(debugLED, led_state);
|
||||
xQueueSendFromISR(enc_queue, &evt, &woken);
|
||||
portYIELD_FROM_ISR(woken);
|
||||
}
|
||||
}
|
||||
|
||||
void encoder_test() {
|
||||
// LED
|
||||
gpio_reset_pin(debugLED);
|
||||
gpio_set_direction(debugLED, GPIO_MODE_OUTPUT);
|
||||
gpio_set_level(debugLED, 0);
|
||||
|
||||
enc_queue = xQueueCreate(32, sizeof(enc_event_t));
|
||||
|
||||
// Init encoder states
|
||||
enc_states[0] = { ENCODER_PIN_A, ENCODER_PIN_B, 0, 0, 0, 0 };
|
||||
enc_states[1] = { InputEnc_PIN_A, InputEnc_PIN_B, 1, 0, 0, 0 };
|
||||
|
||||
// Configure encoder pins
|
||||
gpio_config_t io_conf = {};
|
||||
io_conf.intr_type = GPIO_INTR_ANYEDGE;
|
||||
io_conf.mode = GPIO_MODE_INPUT;
|
||||
io_conf.pull_up_en = GPIO_PULLUP_ENABLE;
|
||||
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
|
||||
io_conf.pin_bit_mask = (1ULL << ENCODER_PIN_A) | (1ULL << ENCODER_PIN_B)
|
||||
| (1ULL << InputEnc_PIN_A) | (1ULL << InputEnc_PIN_B);
|
||||
gpio_config(&io_conf);
|
||||
|
||||
gpio_install_isr_service(ESP_INTR_FLAG_LEVEL1);
|
||||
gpio_isr_handler_add(ENCODER_PIN_A, enc_isr, &enc_states[0]);
|
||||
gpio_isr_handler_add(ENCODER_PIN_B, enc_isr, &enc_states[0]);
|
||||
gpio_isr_handler_add(InputEnc_PIN_A, enc_isr, &enc_states[1]);
|
||||
gpio_isr_handler_add(InputEnc_PIN_B, enc_isr, &enc_states[1]);
|
||||
|
||||
printf("Encoder test running...\n");
|
||||
|
||||
enc_event_t evt;
|
||||
while (true) {
|
||||
if (xQueueReceive(enc_queue, &evt, portMAX_DELAY) == pdTRUE) {
|
||||
printf("[%s] %s\n",
|
||||
evt.encoder_id == 0 ? "TOP " : "BOTTOM",
|
||||
evt.direction == 1 ? "CW (+1)" : "CCW (-1)");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
#ifndef ENCODER_TEST_H
|
||||
#define ENCODER_TEST_H
|
||||
|
||||
void encoder_test();
|
||||
|
||||
#endif
|
||||
@@ -1,54 +0,0 @@
|
||||
#include "servo_test.hpp"
|
||||
#include "defines.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/ledc.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
void servo_test() {
|
||||
// LEDC timer
|
||||
ledc_timer_config_t ledc_timer = {};
|
||||
ledc_timer.speed_mode = LEDC_LOW_SPEED_MODE;
|
||||
ledc_timer.timer_num = LEDC_TIMER_0;
|
||||
ledc_timer.duty_resolution = LEDC_TIMER_16_BIT;
|
||||
ledc_timer.freq_hz = 50;
|
||||
ledc_timer.clk_cfg = LEDC_USE_RC_FAST_CLK;
|
||||
ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer));
|
||||
|
||||
// LEDC channel
|
||||
ledc_channel_config_t ledc_channel = {};
|
||||
ledc_channel.speed_mode = LEDC_LOW_SPEED_MODE;
|
||||
ledc_channel.channel = servoLEDCChannel;
|
||||
ledc_channel.timer_sel = LEDC_TIMER_0;
|
||||
ledc_channel.intr_type = LEDC_INTR_DISABLE;
|
||||
ledc_channel.gpio_num = servoPin;
|
||||
ledc_channel.duty = offSpeed;
|
||||
ledc_channel.hpoint = 0;
|
||||
ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel));
|
||||
|
||||
// Servo switch
|
||||
gpio_reset_pin(servoSwitch);
|
||||
gpio_set_direction(servoSwitch, GPIO_MODE_OUTPUT);
|
||||
gpio_set_level(servoSwitch, 0);
|
||||
|
||||
// Debug LED
|
||||
gpio_reset_pin(debugLED);
|
||||
gpio_set_direction(debugLED, GPIO_MODE_OUTPUT);
|
||||
gpio_set_level(debugLED, 0);
|
||||
|
||||
// Cycle: CW on -> off -> CCW on -> off -> ...
|
||||
static const uint32_t duties[4] = { cwSpeed, offSpeed, ccwSpeed, offSpeed };
|
||||
static const bool switches[4] = { true, false, true, false };
|
||||
|
||||
int step = 0;
|
||||
while (true) {
|
||||
vTaskDelay(pdMS_TO_TICKS(5000));
|
||||
|
||||
ledc_set_duty(LEDC_LOW_SPEED_MODE, servoLEDCChannel, duties[step]);
|
||||
ledc_update_duty(LEDC_LOW_SPEED_MODE, servoLEDCChannel);
|
||||
gpio_set_level(servoSwitch, switches[step] ? 1 : 0);
|
||||
gpio_set_level(debugLED, switches[step] ? 1 : 0);
|
||||
|
||||
step = (step + 1) % 4;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
#ifndef SERVO_TEST_H
|
||||
#define SERVO_TEST_H
|
||||
|
||||
void servo_test();
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user