diff options
Diffstat (limited to 'software/main')
-rw-r--r-- | software/main/CMakeLists.txt | 3 | ||||
-rw-r--r-- | software/main/Kconfig.projbuild | 14 | ||||
-rw-r--r-- | software/main/component.mk | 4 | ||||
-rw-r--r-- | software/main/main.c | 103 | ||||
-rw-r--r-- | software/main/sntp.c | 45 | ||||
-rw-r--r-- | software/main/stepper.c | 99 | ||||
-rw-r--r-- | software/main/sun.c | 100 | ||||
-rw-r--r-- | software/main/wifi.c | 94 |
8 files changed, 462 insertions, 0 deletions
diff --git a/software/main/CMakeLists.txt b/software/main/CMakeLists.txt new file mode 100644 index 0000000..730c43c --- /dev/null +++ b/software/main/CMakeLists.txt @@ -0,0 +1,3 @@ +idf_component_register(SRCS "sun.c" "sntp.c" "wifi.c" "main.c" + "stepper.c" + INCLUDE_DIRS ".")
\ No newline at end of file diff --git a/software/main/Kconfig.projbuild b/software/main/Kconfig.projbuild new file mode 100644 index 0000000..09f282c --- /dev/null +++ b/software/main/Kconfig.projbuild @@ -0,0 +1,14 @@ +menu "Example Configuration" + + config BLINK_GPIO + int "Blink GPIO number" + range 0 34 + default 5 + help + GPIO number (IOxx) to blink on and off. + + Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to blink. + + GPIOs 35-39 are input-only so cannot be used as outputs. + +endmenu diff --git a/software/main/component.mk b/software/main/component.mk new file mode 100644 index 0000000..a98f634 --- /dev/null +++ b/software/main/component.mk @@ -0,0 +1,4 @@ +# +# "main" pseudo-component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/software/main/main.c b/software/main/main.c new file mode 100644 index 0000000..f8099a3 --- /dev/null +++ b/software/main/main.c @@ -0,0 +1,103 @@ +#include <stdio.h> +#include <string.h> +#include <stdbool.h> +#include <time.h> +#include <sys/time.h> +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "driver/gpio.h" +#include "sdkconfig.h" +#include "esp_sleep.h" + +#include "../include/stepper.h" +#include "../include/wifi.h" +#include "../include/sntp.h" +#include "../include/sun.h" + +#define us_in_h 3600000000 +#define us_in_m 60000000 +#define us_in_s 1000000 + +void app_main() { + + // WIFI CONFIGURATION + esp_err_t ret = nvs_flash_init(); + + if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { + ESP_ERROR_CHECK(nvs_flash_erase()); + ret = nvs_flash_init(); + } + ESP_ERROR_CHECK(ret); + + //ESP_LOGI(WIFI_TAG, "ESP_WIFI_MODE_STA"); + wifi_init_sta(); + + // DATE AND TIME CONFIGURATION + time_t now; + struct tm time_info; + time(&now); + localtime_r(&now, &time_info); + + //ESP_LOGI(SNTP_TAG, "Time is not set yet. Connecting to WiFi and getting time over NTP."); + obtain_time(); + + time(&now); + + char time_strftime_buf[64]; + + setenv("TZ", "EST5EDT,M3.2.0/2,M11.1.0", 1); + tzset(); + localtime_r(&now, &time_info); + strftime(time_strftime_buf, sizeof(time_strftime_buf), "%c", &time_info); + //ESP_LOGI(SNTP_TAG, "The current date/time in Toronto is: %s", time_strftime_buf); + + // CALCULATE SUN TIME + struct tm sun_time; + + double sun_time_double = sun_calculation(time_info, LONGITUDE, LATITUDE); + + //printf("sun_time is %f\n", sun_time_double); + + sun_time.tm_hour = floor(sun_time_double); + sun_time.tm_min = floor((sun_time_double - sun_time.tm_hour) * 60); + sun_time.tm_sec = floor((((sun_time_double - sun_time.tm_hour) * 60) - sun_time.tm_min) * 60); + + //TESTING STEPPER MOTOR TIMING + // while(true) { + // stepper_init(); + // stepper_open(); + // stepper_close(); + // stepper_uninit(); + // } + + if(sun_time.tm_hour != time_info.tm_hour) { + //printf("going to deep sleep\n"); + esp_deep_sleep(us_in_h); + } else { + // SLEEP CONFIGURATION + esp_sleep_enable_timer_wakeup(us_in_s); + + // MAIN LOOP + while(true) { + time(&now); + localtime_r(&now, &time_info); + + if(time_info.tm_min < sun_time.tm_min) { + //printf("light sleep\n"); + esp_light_sleep_start(); + } + + if(time_info.tm_hour < 12) { + stepper_init(); + stepper_open(); + stepper_uninit(); + esp_deep_sleep(us_in_m); + } else { + stepper_init(); + stepper_close(); + stepper_uninit(); + esp_deep_sleep(us_in_m); + } + } + } +} diff --git a/software/main/sntp.c b/software/main/sntp.c new file mode 100644 index 0000000..c639a79 --- /dev/null +++ b/software/main/sntp.c @@ -0,0 +1,45 @@ +#include <string.h> +#include <time.h> +#include <sys/time.h> +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" +#include "esp_system.h" +#include "esp_event.h" +#include "esp_log.h" +#include "esp_attr.h" +#include "esp_sleep.h" +#include "nvs_flash.h" +#include "esp_sntp.h" + +#include "../include/sntp.h" + +void obtain_time(void); +void initialize_sntp(void); + +void time_sync_notification_cb(struct timeval *tv) { + ESP_LOGI(SNTP_TAG, "Notification of a time synchronization event"); +} + +void initialize_sntp(void) { + ESP_LOGI(SNTP_TAG, "Initializing SNTP"); + sntp_setoperatingmode(SNTP_OPMODE_POLL); + sntp_setservername(0, "pool.ntp.org"); + sntp_set_time_sync_notification_cb(time_sync_notification_cb); + sntp_init(); +} + +void obtain_time(void) { + initialize_sntp(); + + time_t now = 0; + struct tm timeinfo = {0}; + int retry = 0; + const int retry_count = 10; + while (sntp_get_sync_status() == SNTP_SYNC_STATUS_RESET && ++retry < retry_count) { + ESP_LOGI(SNTP_TAG, "Waiting for system time to be set... (%d/%d)", retry, retry_count); + vTaskDelay(2000 / portTICK_PERIOD_MS); + } + time(&now); + localtime_r(&now, &timeinfo); +}
\ No newline at end of file diff --git a/software/main/stepper.c b/software/main/stepper.c new file mode 100644 index 0000000..71ac227 --- /dev/null +++ b/software/main/stepper.c @@ -0,0 +1,99 @@ +#include <stdio.h> +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "driver/gpio.h" +#include "sdkconfig.h" + +#include "../include/stepper.h" + +// FOR L928 DRIVER +// const uint8_t half_steps_phase[8] = {0x09, 0x01, 0x03, 0x02, 0x06, 0x04, 0x0c, 0x08}; + +// void stepper_init(struct stepper_pins_t *stepper_ptr) { +// gpio_set_direction(stepper_ptr->pin1, GPIO_MODE_OUTPUT); +// gpio_set_direction(stepper_ptr->pin2, GPIO_MODE_OUTPUT); +// gpio_set_direction(stepper_ptr->pin3, GPIO_MODE_OUTPUT); +// gpio_set_direction(stepper_ptr->pin4, GPIO_MODE_OUTPUT); + +// printf("stepper init success\n"); +// } + +// void stepper_uninit(struct stepper_pins_t * stepper_ptr) { +// gpio_set_direction(stepper_ptr->pin1, GPIO_MODE_INPUT); +// gpio_set_direction(stepper_ptr->pin2, GPIO_MODE_INPUT); +// gpio_set_direction(stepper_ptr->pin3, GPIO_MODE_INPUT); +// gpio_set_direction(stepper_ptr->pin4, GPIO_MODE_INPUT); +// } + +// void step(struct stepper_pins_t *stepper_ptr, int step) { +// gpio_set_level(stepper_ptr->pin1, step & 1); +// gpio_set_level(stepper_ptr->pin3, (step & 0b10) >> 1); +// gpio_set_level(stepper_ptr->pin2, (step & 0b100) >> 2); +// gpio_set_level(stepper_ptr->pin4, (step & 0b1000) >> 3); +// } + +// void half_steps(struct stepper_pins_t *stepper_ptr, int steps, bool dir) { +// int step_count; +// int phase_index; + +// if(dir) { +// phase_index = 0; +// for(step_count = 0; step_count < steps; step_count++) { +// if(phase_index > 7) +// phase_index = 0; +// step(stepper_ptr, half_steps_phase[phase_index]); +// vTaskDelay(10 / portTICK_PERIOD_MS); +// phase_index++; +// } +// } else { +// phase_index = 7; +// for(step_count = 0; step_count < steps; step_count++) { +// if (phase_index < 0) +// phase_index = 7; +// step(stepper_ptr, half_steps_phase[phase_index]); +// vTaskDelay(10 / portTICK_PERIOD_MS); +// phase_index--; +// } +// } +// } + +// FOR A4988 DRIVER +const uint8_t step_pin = 19; +const uint8_t dir_pin = 18; +const uint8_t en_pin = 5; + +void stepper_init(void) { + gpio_set_direction(en_pin, GPIO_MODE_OUTPUT); + gpio_set_level(en_pin, HIGH); + gpio_set_direction(dir_pin, GPIO_MODE_OUTPUT); + gpio_set_direction(step_pin, GPIO_MODE_OUTPUT); +} + +void stepper_uninit(void) { + gpio_set_level(en_pin, HIGH); + gpio_set_direction(en_pin, GPIO_MODE_DISABLE); + gpio_set_direction(dir_pin, GPIO_MODE_DISABLE); + gpio_set_direction(step_pin, GPIO_MODE_DISABLE); +} + +void stepper_open(void) { + gpio_set_level(en_pin, LOW); + gpio_set_level(dir_pin, HIGH); + for(uint16_t i = 0; i < 1600; i++) { + gpio_set_level(step_pin, HIGH); + vTaskDelay(10 / portTICK_PERIOD_MS); + gpio_set_level(step_pin, LOW); + vTaskDelay(10 / portTICK_PERIOD_MS); + } +} + +void stepper_close(void) { + gpio_set_level(en_pin, LOW); + gpio_set_level(dir_pin, LOW); + for(uint16_t i = 0; i < 1600; i++) { + gpio_set_level(step_pin, HIGH); + vTaskDelay(10 / portTICK_PERIOD_MS); + gpio_set_level(step_pin, LOW); + vTaskDelay(10 / portTICK_PERIOD_MS); + } +}
\ No newline at end of file diff --git a/software/main/sun.c b/software/main/sun.c new file mode 100644 index 0000000..44d9fa1 --- /dev/null +++ b/software/main/sun.c @@ -0,0 +1,100 @@ +#include <stdio.h> +#include <string.h> +#include <stdbool.h> +#include <math.h> +#include <time.h> + +#include "../include/sun.h" + +double deg_to_rad(double angle) { + return angle * (M_PI / 180); +} + +double rad_to_deg(double angle) { + return angle * (180 / M_PI); +} + +double sun_calculation(struct tm curr_time, double longitude, double latitude) { + // Calulate day of the year + int day_in_year = floor(275 * (curr_time.tm_mon + 1) / 9) - + ((floor(((curr_time.tm_mon + 1) + 9) / 12)) * + (1 + floor(((curr_time.tm_year + 1900) - 4 * floor((curr_time.tm_year + 1900) / 4) + 2) / 3))) + + curr_time.tm_mday - 30; + + // Longitude to hour, get approx time + double long_hour = longitude / 15; + double approx_time; + + if (curr_time.tm_hour < 12) + approx_time = day_in_year + ((6 - long_hour) / 24); + else + approx_time = day_in_year + ((18 - long_hour) / 24); + + // Sun's mean anomaly + double mean_anomaly = (0.9856 * approx_time) - 3.289; + + // Sun's true longitude + double true_longitude = mean_anomaly + (1.916 * sin(deg_to_rad(mean_anomaly)) + + (0.02 * sin(deg_to_rad(2 * mean_anomaly)) + 282.634)); + + // Keep longitude between [0, 360) + if (true_longitude >= 360) + true_longitude -= 360; + else if (true_longitude < 0) + true_longitude += 360; + + // Sun's right ascension + double right_ascension = rad_to_deg(atan(0.91764 * tan(deg_to_rad(true_longitude)))); + + // Keep Right Ascension between [0, 360) + if (right_ascension >= 360) + right_ascension -= 360; + else if (right_ascension < 0) + right_ascension += 360; + + // Quadrant calibration + int longitude_quadrant = (floor(true_longitude / 90)) * 90; + int right_ascension_quadrant = (floor(right_ascension / 90)) * 90; + + right_ascension += (longitude_quadrant - right_ascension_quadrant); + + right_ascension /= 15; // convert to hours + + // Calculate Sun's declination + double sin_declination = 0.39782 * sin(deg_to_rad(true_longitude)); + double cos_declination = cos(asin(sin_declination)); + + // Calculate Sun's local hour angle + double sun_hour; + + double cos_hour = (-0.01454 - (sin_declination * sin(deg_to_rad(latitude)))) / + (cos_declination * cos(deg_to_rad(latitude))); + + if (curr_time.tm_hour < 12) + sun_hour = 360 - rad_to_deg(acos(cos_hour)); + else + sun_hour = rad_to_deg(acos(cos_hour)); + + sun_hour /= 15; // convert to hours + + // Local time + double local_mean_time = sun_hour + right_ascension - (0.06571 * approx_time) - 6.622; + + // Adjust to UTC + double sun_UTC = local_mean_time - long_hour; + + // Keep Sun UTC between [0, 24) + if (sun_UTC >= 24) + sun_UTC -= 24; + else if (sun_UTC < 0) + sun_UTC += 24; + + double sun_time = sun_UTC + LOCAL_OFFSET; + + if (sun_time < 0) + return (24 + sun_time); + else if (sun_time > 24) + return (sun_time - 24); + else + return sun_time; +}
\ No newline at end of file diff --git a/software/main/wifi.c b/software/main/wifi.c new file mode 100644 index 0000000..f319257 --- /dev/null +++ b/software/main/wifi.c @@ -0,0 +1,94 @@ +#include <string.h> +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" +#include "esp_system.h" +#include "esp_wifi.h" +#include "esp_event.h" +#include "esp_log.h" +#include "nvs_flash.h" + +#include "lwip/err.h" +#include "lwip/sys.h" + +#include "../include/wifi.h" + +void event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) { + if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { + esp_wifi_connect(); + } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { + if (s_retry_num < ESP_MAXIMUM_RETRY) { + esp_wifi_connect(); + s_retry_num++; + ESP_LOGI(WIFI_TAG, "retry to connect to the AP"); + } else { + xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT); + } + ESP_LOGI(WIFI_TAG,"connect to the AP fail"); + } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { + ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data; + ESP_LOGI(WIFI_TAG, "got ip:%s", + ip4addr_ntoa(&event->ip_info.ip)); + s_retry_num = 0; + xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT); + } +} + +void wifi_init_sta() { + s_wifi_event_group = xEventGroupCreate(); + + tcpip_adapter_init(); + + ESP_ERROR_CHECK(esp_event_loop_create_default()); + + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_wifi_init(&cfg)); + + ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL)); + ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL)); + + wifi_config_t wifi_config = { + .sta = { + .ssid = ESP_WIFI_SSID, + .password = ESP_WIFI_PASS, + /* Setting a password implies station will connect to all security modes including WEP/WPA. + * However these modes are deprecated and not advisable to be used. Incase your Access point + * doesn't support WPA2, these mode can be enabled by commenting below line */ + .threshold.authmode = WIFI_AUTH_WPA2_PSK, + + .pmf_cfg = { + .capable = true, + .required = false + }, + }, + }; + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) ); + ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) ); + ESP_ERROR_CHECK(esp_wifi_start() ); + + ESP_LOGI(WIFI_TAG, "wifi_init_sta finished."); + + /* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum + * number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */ + EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group, + WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, + pdFALSE, + pdFALSE, + portMAX_DELAY); + + /* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually + * happened. */ + if (bits & WIFI_CONNECTED_BIT) { + ESP_LOGI(WIFI_TAG, "connected to ap SSID:%s password:%s", + ESP_WIFI_SSID, ESP_WIFI_PASS); + } else if (bits & WIFI_FAIL_BIT) { + ESP_LOGI(WIFI_TAG, "Failed to connect to SSID:%s, password:%s", + ESP_WIFI_SSID, ESP_WIFI_PASS); + } else { + ESP_LOGE(WIFI_TAG, "UNEXPECTED EVENT"); + } + + ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler)); + ESP_ERROR_CHECK(esp_event_handler_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler)); + vEventGroupDelete(s_wifi_event_group); +}
\ No newline at end of file |