2024-07-31 22:13:10 +02:00

184 lines
5.7 KiB
C

/*
weather.c - Weather data retrieval from api.openweathermap.org
This file is part of the ESP32 Everest Run project
https://github.com/krzychb/esp32-everest-run
Copyright (c) 2016 Krzysztof Budzynski <krzychb@gazeta.pl>
This work is licensed under the Apache License, Version 2.0, January 2004
See the file LICENSE for details.
*/
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_log.h"
#include <stdio.h>
#include <string.h>
#include "weather.h"
#include "http.h"
#include "jsmn.h"
static const char* TAG = "Weather";
/* Constants that aren't configurable in menuconfig
Typically only LOCATION_ID may need to be changed
*/
#define WEB_SERVER "api.openweathermap.org"
#define WEB_URL "http://api.openweathermap.org/data/2.5/weather"
// Location ID to get the weather data for
//#define LOCATION_ID "756135"
#define LATITUDE "49.22017054145735"
#define LONGITUDE "3.92188756221628"
// The API key below is configurable in menuconfig
#define OPENWEATHERMAP_API_KEY "24d95e3a2c27a1843590b91bf2cbf37b__"
static const char *get_request = "GET " WEB_URL"?lat="LATITUDE"&lon="LONGITUDE"&appid="OPENWEATHERMAP_API_KEY" HTTP/1.1\n"
"Host: "WEB_SERVER"\n"
"Connection: close\n"
"User-Agent: esp-idf/1.0 esp32\n"
"\n";
static weather_data weather;
static http_client_data http_client = {0};
/* Collect chunks of data received from server
into complete message and save it in proc_buf
*/
static void process_chunk(uint32_t *args)
{
http_client_data* client = (http_client_data*)args;
int proc_buf_new_size = client->proc_buf_size + client->recv_buf_size;
char *copy_from;
if (client->proc_buf == NULL){
client->proc_buf = malloc(proc_buf_new_size);
copy_from = client->proc_buf;
} else {
proc_buf_new_size -= 1; // chunks of data are '\0' terminated
client->proc_buf = realloc(client->proc_buf, proc_buf_new_size);
copy_from = client->proc_buf + proc_buf_new_size - client->recv_buf_size;
}
if (client->proc_buf == NULL) {
ESP_LOGE(TAG, "Failed to allocate memory");
}
client->proc_buf_size = proc_buf_new_size;
memcpy(copy_from, client->recv_buf, client->recv_buf_size);
}
static int jsoneq(const char *json, jsmntok_t *tok, const char *s) {
if (tok->type == JSMN_STRING && (int) strlen(s) == tok->end - tok->start &&
strncmp(json + tok->start, s, tok->end - tok->start) == 0) {
return 0;
}
return -1;
}
static bool process_response_body(const char * body)
{
/* Using great little JSON parser http://zserge.com/jsmn.html
find specific weather information:
- Humidity,
- Temperature,
- Pressure
in HTTP response body that happens to be a JSON string
Return true if phrasing was successful or false if otherwise
*/
#define JSON_MAX_TOKENS 100
jsmn_parser parser;
jsmntok_t t[JSON_MAX_TOKENS];
jsmn_init(&parser);
ESP_LOGE(TAG,"%s",body);
int r = jsmn_parse(&parser, body, strlen(body), t, JSON_MAX_TOKENS);
if (r < 0) {
ESP_LOGE(TAG, "JSON parse error %d", r);
return false;
}
if (r < 1 || t[0].type != JSMN_OBJECT) {
ESP_LOGE(TAG, "JSMN_OBJECT expected");
return false;
} else {
ESP_LOGI(TAG, "Token(s) found %d", r);
char subbuff[8];
int str_length;
for (int i = 1; i < r; i++) {
if (jsoneq(body, &t[i], "humidity") == 0) {
str_length = t[i+1].end - t[i+1].start;
memcpy(subbuff, body + t[i+1].start, str_length);
subbuff[str_length] = '\0';
weather.humidity = atoi(subbuff);
i++;
} else if (jsoneq(body, &t[i], "temp") == 0) {
str_length = t[i+1].end - t[i+1].start;
memcpy(subbuff, body + t[i+1].start, str_length);
subbuff[str_length] = '\0';
weather.temperature = atof(subbuff);
i++;
} else if (jsoneq(body, &t[i], "pressure") == 0) {
str_length = t[i+1].end - t[i+1].start;
memcpy(subbuff, body + t[i+1].start, str_length);
subbuff[str_length] = '\0';
weather.pressure = atof(subbuff);
i++;
}
}
return true;
}
}
static void disconnected(uint32_t *args)
{
http_client_data* client = (http_client_data*)args;
bool weather_data_phrased = false;
const char * response_body = find_response_body(client->proc_buf);
if (response_body) {
weather_data_phrased = process_response_body(response_body);
} else {
ESP_LOGE(TAG, "No HTTP header found");
}
free(client->proc_buf);
client->proc_buf = NULL;
client->proc_buf_size = 0;
// execute callback if data was retrieved
if (weather_data_phrased) {
if (weather.data_retreived_cb) {
weather.data_retreived_cb((uint32_t*) &weather);
}
}
ESP_LOGD(TAG, "Free heap %u", xPortGetFreeHeapSize());
}
static void http_request_task(void *pvParameter)
{
while(1) {
http_client_request(&http_client, WEB_SERVER, get_request);
vTaskDelay(weather.retreival_period / portTICK_PERIOD_MS);
}
}
void on_weather_data_retrieval(weather_data_callback data_retreived_cb)
{
weather.data_retreived_cb = data_retreived_cb;
}
void initialise_weather_data_retrieval(unsigned long retreival_period)
{
weather.retreival_period = retreival_period;
http_client_on_process_chunk(&http_client, process_chunk);
http_client_on_disconnected(&http_client, disconnected);
xTaskCreate(&http_request_task, "http_request_task", 2 * 2048, NULL, 5, NULL);
ESP_LOGI(TAG, "HTTP request task started");
}