322 lines
9.2 KiB
C
322 lines
9.2 KiB
C
#include "esp_task_wdt.h"
|
|
#include "i2c.h"
|
|
#include "storage.h"
|
|
#include "uart_comms.h"
|
|
#include "esp_err.h"
|
|
#include "esp_log.h"
|
|
#include "endian.h"
|
|
#include "control_fsm.h"
|
|
#include "power_mgmt.h"
|
|
#include "rtc.h"
|
|
#include "sensors.h"
|
|
#include "solar.h"
|
|
#include "rf_433.h"
|
|
#include "webserver.h"
|
|
#include "version.h"
|
|
#include <string.h>
|
|
|
|
#define TAG "MAIN"
|
|
|
|
int64_t last_log_time = 0;
|
|
esp_err_t send_log() {
|
|
// >Hqfffflccc
|
|
uint8_t entry[32] = {0};
|
|
entry[0] = 32;
|
|
|
|
// Pack 64-bit timestamp into bytes 1-8
|
|
uint64_t be_timestamp = rtc_get_ms();
|
|
memcpy(&entry[1], &be_timestamp, 8);
|
|
|
|
// Pack 32-bit voltages/currents into bytes 9-24
|
|
float be_voltage = get_battery_V();
|
|
memcpy(&entry[9], &be_voltage, 4);
|
|
float be_current1 = get_bridge_A(BRIDGE_DRIVE);
|
|
memcpy(&entry[13], &be_current1, 4);
|
|
float be_current2 = get_bridge_A(BRIDGE_JACK);
|
|
memcpy(&entry[17], &be_current2, 4);
|
|
float be_current3 = get_bridge_A(BRIDGE_AUX);
|
|
memcpy(&entry[21], &be_current3, 4);
|
|
|
|
int32_t be_counter = get_sensor_counter(SENSOR_DRIVE);
|
|
memcpy(&entry[25], &be_counter, 4);
|
|
|
|
entry[29] = get_sensor(SENSOR_SAFETY);
|
|
entry[30] = get_sensor(SENSOR_DRIVE);
|
|
entry[31] = fsm_get_state();
|
|
|
|
last_log_time = esp_timer_get_time();
|
|
|
|
return write_log(LOG_TYPE_DATA, entry, 32);
|
|
}
|
|
|
|
|
|
typedef enum {
|
|
LED_STATE_DRIVING,
|
|
LED_STATE_ERROR,
|
|
LED_STATE_AWAKE,
|
|
LED_STATE_CANCELLING,
|
|
LED_STATE_ERRORED,
|
|
LED_STATE_START1,
|
|
LED_STATE_START2,
|
|
LED_STATE_START3,
|
|
LED_STATE_START4,
|
|
LED_STATE_BOOTING
|
|
} led_state_t;
|
|
|
|
void driveLEDs(led_state_t state) {
|
|
uint8_t patterns[5][12] = {
|
|
{1,3,7,6,4,0},
|
|
{0b101,0b001},
|
|
{1,1,1,1,1,1, 1,1,1,3},
|
|
{4,2},
|
|
{0b001, 0b101},
|
|
};
|
|
switch(state) {
|
|
case LED_STATE_DRIVING:
|
|
i2c_set_led1(patterns[state][(esp_timer_get_time()/100000) % 6]);
|
|
break;
|
|
case LED_STATE_ERROR:
|
|
//ESP_LOGE(TAG, "SOME SORT OF ERROR");
|
|
i2c_set_led1(patterns[state][(esp_timer_get_time()/1000000) % 2]);
|
|
break;
|
|
case LED_STATE_AWAKE:
|
|
i2c_set_led1(patterns[state][(esp_timer_get_time()/200000) % 10]);
|
|
break;
|
|
case LED_STATE_CANCELLING:
|
|
i2c_set_led1(patterns[state][(esp_timer_get_time()/200000) % 2]);
|
|
break;
|
|
|
|
case LED_STATE_ERRORED:
|
|
i2c_set_led1(patterns[state][(esp_timer_get_time()/200000) % 2]);
|
|
break;
|
|
|
|
case LED_STATE_BOOTING:
|
|
i2c_set_led1(0b001);
|
|
break;
|
|
|
|
case LED_STATE_START1:
|
|
i2c_set_led1(0b000);
|
|
break;
|
|
case LED_STATE_START2:
|
|
i2c_set_led1(0b001);
|
|
break;
|
|
case LED_STATE_START3:
|
|
i2c_set_led1(0b011);
|
|
break;
|
|
case LED_STATE_START4:
|
|
i2c_set_led1(0b111);
|
|
break;
|
|
}
|
|
}
|
|
|
|
RTC_DATA_ATTR bool first_boot = true;
|
|
|
|
void app_main(void) {
|
|
esp_task_wdt_add(NULL);
|
|
|
|
if (rtc_xtal_init() != ESP_OK) ESP_LOGE(TAG, "RTC FAILED");
|
|
|
|
// Say hello; turn on the lights
|
|
esp_sleep_wakeup_cause_t cause = rtc_wakeup_cause();
|
|
if (i2c_init() != ESP_OK) ESP_LOGE(TAG, "I2C FAILED");
|
|
i2c_set_relays(0);
|
|
driveLEDs(LED_STATE_BOOTING);
|
|
|
|
ESP_LOGI(TAG, "Firmware: %s", FIRMWARE_STRING);
|
|
ESP_LOGI(TAG, "Version: %s", FIRMWARE_VERSION);
|
|
ESP_LOGI(TAG, "Branch: %s", FIRMWARE_BRANCH);
|
|
ESP_LOGI(TAG, "Built: %s", BUILD_DATE);
|
|
|
|
|
|
// Check for factory reset condition: Cold boot + button held
|
|
// This is a cold boot (power-on or hard reset)
|
|
// Check if button is being held (pin is LOW)
|
|
if (first_boot && gpio_get_level(GPIO_NUM_13) == 0) {
|
|
ESP_LOGW(TAG, "FACTORY RESET TRIGGERED - Button held on cold boot");
|
|
|
|
// Flash LED pattern to indicate factory reset
|
|
for (int i = 0; i < 10; i++) {
|
|
i2c_set_led1(0b111);
|
|
vTaskDelay(pdMS_TO_TICKS(100));
|
|
i2c_set_led1(0b000);
|
|
vTaskDelay(pdMS_TO_TICKS(100));
|
|
}
|
|
|
|
// Initialize minimal components needed for factory reset
|
|
if (storage_init() != ESP_OK) ESP_LOGE(TAG, "STORAGE FAILED");
|
|
|
|
// Perform factory reset
|
|
esp_err_t reset_err = factory_reset();
|
|
if (reset_err == ESP_OK) {
|
|
ESP_LOGI(TAG, "Factory reset completed successfully");
|
|
// Flash success pattern
|
|
for (int i = 0; i < 5; i++) {
|
|
i2c_set_led1(0b010);
|
|
vTaskDelay(pdMS_TO_TICKS(200));
|
|
i2c_set_led1(0b000);
|
|
vTaskDelay(pdMS_TO_TICKS(200));
|
|
}
|
|
} else {
|
|
ESP_LOGE(TAG, "Factory reset failed!");
|
|
// Flash error pattern
|
|
for (int i = 0; i < 5; i++) {
|
|
i2c_set_led1(0b100);
|
|
vTaskDelay(pdMS_TO_TICKS(200));
|
|
i2c_set_led1(0b000);
|
|
vTaskDelay(pdMS_TO_TICKS(200));
|
|
}
|
|
}
|
|
|
|
// Reboot the system
|
|
ESP_LOGI(TAG, "Rebooting system...");
|
|
vTaskDelay(pdMS_TO_TICKS(1000));
|
|
esp_restart();
|
|
}
|
|
|
|
first_boot = false;
|
|
|
|
// Every boot we load parameters and monitor solar, no matter what
|
|
if (adc_init() != ESP_OK) ESP_LOGE(TAG, "ADC FAILED");
|
|
if (storage_init() != ESP_OK) ESP_LOGE(TAG, "STORAGE FAILED");
|
|
if (log_init() != ESP_OK) ESP_LOGE(TAG, "LOG FAILED");
|
|
if (solar_run_fsm() != ESP_OK) ESP_LOGE(TAG, "SOLAR FAILED");
|
|
// TODO: Do a 12V check and enter deep sleep if there's a problem
|
|
|
|
|
|
send_log();
|
|
|
|
//write_dummy_log_1();
|
|
|
|
// Check wake reasons
|
|
// If button held, we stay #woke
|
|
// If not it must've been the RTC - check alarms
|
|
// If there's an alarm or the button was held, do a full boot
|
|
// Full boot will handle things from there
|
|
|
|
// only truly wake up if we saw it on EXT0, or from alarm
|
|
/*if (!rtc_is_set()) {
|
|
//ESP_LOGI("MAIN", "RTC is not set. Can't sleep til then.");
|
|
} else */if (cause == ESP_SLEEP_WAKEUP_EXT0) {
|
|
ESP_LOGI("MAIN", "Woke from button press");
|
|
} else {
|
|
if (!rtc_alarm_tripped()) {
|
|
//enter_deep_sleep();
|
|
}
|
|
}
|
|
|
|
/*** FULL BOOT ***/
|
|
//if (uart_init() != ESP_OK) ESP_LOGE(TAG, "UART FAILED");
|
|
if (power_init() != ESP_OK) ESP_LOGE(TAG, "POWER FAILED");
|
|
if (rf_433_init() != ESP_OK) ESP_LOGE(TAG, "RF FAILED");
|
|
if (fsm_init() != ESP_OK) ESP_LOGE(TAG, "FSM FAILED");
|
|
if (sensors_init() != ESP_OK) ESP_LOGE(TAG, "SENSORS FAILED");
|
|
if (webserver_init() != ESP_OK) ESP_LOGE(TAG, "WEBSERVER FAILED");
|
|
|
|
/*** MAIN LOOP ***/
|
|
TickType_t xLastWakeTime = xTaskGetTickCount();
|
|
const TickType_t xFrequency = pdMS_TO_TICKS(50);
|
|
|
|
/*while(true) {
|
|
ESP_LOGI(TAG, "TICK");
|
|
vTaskDelayUntil(&xLastWakeTime, pdMS_TO_TICKS(1000));
|
|
esp_task_wdt_reset();
|
|
}*/
|
|
|
|
while(true) {
|
|
vTaskDelayUntil(&xLastWakeTime, xFrequency);
|
|
|
|
i2c_poll_buttons();
|
|
|
|
if (i2c_get_button_state(0))
|
|
rtc_reset_shutdown_timer();
|
|
|
|
switch (fsm_get_state()) {
|
|
case STATE_IDLE:
|
|
// LED cue for user
|
|
if (i2c_get_button_ms(0) > 1600){
|
|
driveLEDs(LED_STATE_START4);
|
|
} else if (i2c_get_button_ms(0) > 1100){
|
|
driveLEDs(LED_STATE_START3);
|
|
} else if (i2c_get_button_ms(0) > 600){
|
|
driveLEDs(LED_STATE_START2);
|
|
} else if (i2c_get_button_ms(0) > 100){
|
|
driveLEDs(LED_STATE_START1);
|
|
} else {
|
|
if (
|
|
rtc_is_set() &&
|
|
!efuse_is_tripped(BRIDGE_JACK) &&
|
|
!efuse_is_tripped(BRIDGE_AUX) &&
|
|
!efuse_is_tripped(BRIDGE_DRIVE) &&
|
|
fsm_get_error() == ESP_OK
|
|
) {
|
|
driveLEDs(LED_STATE_AWAKE);
|
|
} else {
|
|
driveLEDs(LED_STATE_ERROR);
|
|
}
|
|
}
|
|
|
|
// when not actively moving we log at a low frequency
|
|
if (isRunning() || (esp_timer_get_time() > last_log_time + DEEP_SLEEP_US))
|
|
send_log();
|
|
|
|
if(i2c_get_button_ms(0) > 2100)
|
|
fsm_request(FSM_CMD_START);
|
|
break;
|
|
case STATE_UNDO_JACK:
|
|
case STATE_UNDO_JACK_START:
|
|
// it's running the jack, but undoing
|
|
send_log();
|
|
driveLEDs(LED_STATE_CANCELLING);
|
|
if (i2c_get_button_tripped(0)) {
|
|
ESP_LOGI(TAG, "AAAAH STOP!!!");
|
|
fsm_request(FSM_CMD_STOP);
|
|
}
|
|
break;
|
|
|
|
case STATE_CALIBRATE_JACK_DELAY:
|
|
send_log();
|
|
if (i2c_get_button_tripped(0))
|
|
fsm_request(FSM_CMD_CALIBRATE_JACK_START);
|
|
break;
|
|
case STATE_CALIBRATE_JACK_MOVE:
|
|
send_log();
|
|
if (i2c_get_button_tripped(0))
|
|
fsm_request(FSM_CMD_CALIBRATE_JACK_END);
|
|
break;
|
|
|
|
|
|
case STATE_CALIBRATE_DRIVE_DELAY:
|
|
send_log();
|
|
if (i2c_get_button_tripped(0))
|
|
fsm_request(FSM_CMD_CALIBRATE_DRIVE_START);
|
|
break;
|
|
case STATE_CALIBRATE_DRIVE_MOVE:
|
|
send_log();
|
|
if (i2c_get_button_tripped(0))
|
|
fsm_request(FSM_CMD_CALIBRATE_DRIVE_END);
|
|
break;
|
|
|
|
default:
|
|
// it's running in every other case
|
|
send_log();
|
|
driveLEDs(LED_STATE_DRIVING);
|
|
if (i2c_get_button_tripped(0)) {
|
|
fsm_request(FSM_CMD_UNDO);
|
|
}
|
|
break;
|
|
}
|
|
|
|
|
|
|
|
|
|
if (rtc_alarm_tripped()) {
|
|
fsm_request(FSM_CMD_START);
|
|
rtc_schedule_next_alarm();
|
|
}
|
|
|
|
solar_run_fsm();
|
|
|
|
rtc_check_shutdown_timer();
|
|
esp_task_wdt_reset();
|
|
}
|
|
} |