#include #include "display.h" #include "https_request.h" #include "stock.h" static SemaphoreHandle_t mutex = NULL; static unsigned int current_symbol = 0; static stock_data symbols[] = { { .name = "META", .current = 0.0, .delta = 0.0, .color = { .r = 0x00, .g = 0x81, .b = 0xFB } }, { .name = "AAPL", .current = 0.0, .delta = 0.0, .color = { .r = 0xA2, .g = 0xAA, .b = 0xAD } }, { .name = "MSFT", .current = 0.0, .delta = 0.0, .color = { .r = 0x00, .g = 0xA3, .b = 0xEE } }, { .name = "GOOGL", .current = 0.0, .delta = 0.0, .color = { .r = 0x42, .g = 0x85, .b = 0xF4 } }, { .name = "NVDA", .current = 0.0, .delta = 0.0, .color = { .r = 0x76, .g = 0xB9, .b = 0x00 } } }; esp_err_t init_stock() { mutex = xSemaphoreCreateMutex(); return ESP_OK; } esp_err_t update_stock_price(unsigned int index, float current, float delta) { const char* TAG = "update_stock_price"; if(xSemaphoreTake(mutex, 5000 / portTICK_PERIOD_MS ) == pdTRUE) { symbols[index].current = current; symbols[index].delta = delta; xSemaphoreGive(mutex); } else { ESP_LOGE(TAG, "Failed to acquire stock update mutex"); return ESP_ERR_NOT_FINISHED; } return ESP_OK; } esp_err_t parse_JSON(const char* json, float* current, float* delta) { cJSON *root = cJSON_Parse(json); if (cJSON_GetObjectItem(root, "c")) { *current = (float)cJSON_GetObjectItem(root,"c")->valuedouble; } else { cJSON_Delete(root); return ESP_ERR_NOT_FOUND; } if (cJSON_GetObjectItem(root, "d")) { *delta = (float)cJSON_GetObjectItem(root,"d")->valuedouble; } else { cJSON_Delete(root); return ESP_ERR_NOT_FOUND; } cJSON_Delete(root); return ESP_OK; } void display_stock_task(void* arg) { uint8_t display_percentage = 0; uint8_t alt_count = 0; char stock[16]; char delta_perc[16]; char delta[16]; const char* TAG = "display_stock_task"; ESP_LOGI(TAG, "display_stock_task start"); for(;;) { float c = 0.0; float d = 0.0; if(xSemaphoreTake(mutex, portMAX_DELAY ) == pdTRUE) { c = symbols[current_symbol].current; d = symbols[current_symbol].delta; xSemaphoreGive(mutex); } drawText(19, 0, symbols[current_symbol].name, strlen(symbols[current_symbol].name), symbols[current_symbol].color.r, symbols[current_symbol].color.g, symbols[current_symbol].color.b); sprintf(stock, "$%.02f", c); sprintf(delta, "%+.02f", d); sprintf(delta_perc, "%+0.04f%%", -100* (1 - (c / (c-d)))); // META drawText((PANEL_RES_X - strlen(stock))/4, 8, stock, strlen(stock), d < 0 ? 0xFF : 0x00, d < 0 ? 0x00 : 0xFF, 0x00); // Arrow if(d < 0) { dma_display->drawIcon(arrow_down_icon, 1, 8, 6, 8); } else { dma_display->drawIcon(arrow_up_icon, 1, 8, 6, 8); } // Delta if(display_percentage) { drawText((PANEL_RES_X - strlen(delta_perc))/4, 16, delta_perc, strlen(delta_perc), d < 0 ? 0xFF : 0x00, d < 0 ? 0x00 : 0xFF, 0x00); } else { drawText((PANEL_RES_X - strlen(delta))/4, 16, delta, strlen(delta), d < 0 ? 0xFF : 0x00, d < 0 ? 0x00 : 0xFF, 0x00); } vTaskDelay(ALTERNATE_TIME * 1000 / portTICK_PERIOD_MS); display_percentage ^= 1; dma_display->clearScreen(); if(++alt_count == ALTERNATIONS_PER_SYMBOL) { current_symbol = (current_symbol + 1) % (sizeof(symbols) / sizeof(stock_data)); alt_count = 0; } } } esp_err_t fetch_stock_data(const char* symbol, float* current, float* delta) { const char* TAG = "fetch_stock_data"; int code = 0; char** response_headers = nullptr; char* response = nullptr; char* headers[2] = {(char*)("X-Finnhub-Token=" FINNHUB_API_KEY), NULL}; size_t url_len = strlen(BASE_URL) + strlen(symbol) + 1; char* url = (char*)malloc(url_len); if(url == NULL) { ESP_LOGE(TAG, "Unable to alloc memory for URL"); return ESP_FAIL; } snprintf(url, url_len, "%s%s", BASE_URL, symbol); esp_err_t err = do_https_request(url, HTTP_METHOD_GET, (const char**)headers, &response_headers, &response, &code); free(url); if(err != ESP_OK) { ESP_LOGE(TAG, "Unable to obtain stock data: err=%d", err); return err; } else if(code != 200) { ESP_LOGE(TAG, "API returned code: code=%d", code); free_https_response(&response_headers, &response); return ESP_FAIL; } else { err = parse_JSON(response, current, delta); if(err != ESP_OK) { ESP_LOGE(TAG, "Error parsing JSON: err=%d", err); } } free_https_response(&response_headers, &response); return ESP_OK; } void fetch_stock_task(void* arg) { const char* TAG = "fetch_stock_task"; ESP_LOGI(TAG, "fetch_stock_task start"); for(;;) { for(unsigned int i = 0; i < (sizeof(symbols)/sizeof(stock_data)); ++i) { float current; float delta; esp_err_t err = fetch_stock_data(symbols[i].name, ¤t, &delta); if(err != ESP_OK) { ESP_LOGE(TAG, "Error parsing JSON: err=%d", err); } else { int retries = 0; while(retries++ != MAX_UPDATE_RETRIES) { update_stock_price(i, current, delta); } } vTaskDelay(API_DELAY * 1000 / portTICK_PERIOD_MS); } vTaskDelay(FETCH_TIME * 1000 / portTICK_PERIOD_MS); } }