nettoyage

This commit is contained in:
Marc 2024-08-02 07:50:53 +02:00
parent 5a33c7d1f7
commit 84f72fa3ef
15 changed files with 96 additions and 941 deletions

View File

@ -1,2 +0,0 @@
idf_component_register(SRCS "http.c"
INCLUDE_DIRS "include")

View File

@ -1,159 +0,0 @@
/*
http.c - HTTP request routines
File based on https://github.com/espressif/esp-idf/tree/master/examples/03_http_request
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 <string.h>
#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include "lwip/netdb.h"
#include "lwip/dns.h"
#include "http.h"
#define RECV_BUFFER_SIZE 64
static const char* TAG = "HTTP";
void http_client_on_connected(http_client_data *client, http_callback http_on_connected_cb)
{
client->http_connected_cb = http_on_connected_cb;
}
void http_client_on_process_chunk(http_client_data *client, http_callback http_process_chunk_cb)
{
client->http_process_chunk_cb = http_process_chunk_cb;
}
void http_client_on_disconnected(http_client_data *client, http_callback http_disconnected_cb)
{
client->http_disconnected_cb = http_disconnected_cb;
}
esp_err_t http_client_request(http_client_data *client, const char *web_server, const char *request_string)
{
const struct addrinfo hints = {
.ai_family = AF_INET,
.ai_socktype = SOCK_STREAM,
};
struct addrinfo *res;
struct in_addr *addr;
int s, r;
char recv_buf[RECV_BUFFER_SIZE];
client->recv_buf = recv_buf;
client->recv_buf_size = sizeof(recv_buf);
int err = getaddrinfo(web_server, "80", &hints, &res);
if (err != 0 || res == NULL) {
ESP_LOGE(TAG, "DNS lookup failed err=%d res=%p", err, res);
vTaskDelay(1000 / portTICK_PERIOD_MS);
return ESP_ERR_HTTP_DNS_LOOKUP_FAILED;
}
/* Code to print the resolved IP.
Note: inet_ntoa is non-reentrant, look at ipaddr_ntoa_r for "real" code
*/
addr = &((struct sockaddr_in *)res->ai_addr)->sin_addr;
ESP_LOGI(TAG, "DNS lookup succeeded. IP=%s", inet_ntoa(*addr));
s = socket(res->ai_family, res->ai_socktype, 0);
if (s < 0) {
ESP_LOGE(TAG, "... Failed to allocate socket.");
freeaddrinfo(res);
vTaskDelay(1000 / portTICK_PERIOD_MS);
return ESP_ERR_HTTP_FAILED_TO_ALLOCATE_SOCKET;
}
ESP_LOGI(TAG, "... allocated socket");
if (connect(s, res->ai_addr, res->ai_addrlen) != 0) {
ESP_LOGE(TAG, "... socket connect failed errno=%d", errno);
close(s);
freeaddrinfo(res);
vTaskDelay(4000 / portTICK_PERIOD_MS);
return ESP_ERR_HTTP_SOCKET_CONNECT_FAILED;
}
ESP_LOGI(TAG, "... connected");
freeaddrinfo(res);
if (client->http_connected_cb) {
client->http_connected_cb((uint32_t*) client);
}
if (write(s, request_string, strlen(request_string)) < 0) {
ESP_LOGE(TAG, "... socket send failed");
close(s);
vTaskDelay(4000 / portTICK_PERIOD_MS);
return ESP_ERR_HTTP_SOCKET_SEND_FAILED;
}
ESP_LOGI(TAG, "... socket send success");
/* Read HTTP response */
do {
bzero(recv_buf, sizeof(recv_buf));
r = read(s, recv_buf, sizeof(recv_buf)-1);
if (client->http_process_chunk_cb) {
client->http_process_chunk_cb((uint32_t*) client);
}
} while (r > 0);
ESP_LOGI(TAG, "... done reading from socket. Last read return=%d errno=%d", r, errno);
close(s);
if (client->http_disconnected_cb) {
client->http_disconnected_cb((uint32_t*) client);
}
return ESP_OK;
}
/* Out of HTTP response return pointer to response body
Function return NULL if end of header cannot be identified
*/
const char* find_response_body(char * response)
{
// Identify end of the response headers
// http://stackoverflow.com/questions/11254037/how-to-know-when-the-http-headers-part-is-ended
char * eol; // end of line
char * bol; // beginning of line
bool nheaderfound = false; // end of response headers has been found
bol = response;
while ((eol = index(bol, '\n')) != NULL) {
// update bol based upon the value of eol
bol = eol + 1;
// test if end of headers has been reached
if ( (!(strncmp(bol, "\r\n", 2))) || (!(strncmp(bol, "\n", 1))) )
{
// note that end of headers has been found
nheaderfound = true;
// update the value of bol to reflect the beginning of the line
// immediately after the headers
if (bol[0] != '\n') {
bol += 1;
}
bol += 1;
break;
}
}
if (nheaderfound) {
return bol;
} else {
return NULL;
}
}

View File

@ -1,49 +0,0 @@
/*
http.h - HTTP request routines
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.
*/
#ifndef HTTP_H
#define HTTP_H
#ifdef __cplusplus
extern "C" {
#endif
#include "esp_err.h"
typedef void (*http_callback)(uint32_t *args);
typedef struct {
char *recv_buf; /*!< Pointer to a receive buffer, data from the socket are collected here */
int recv_buf_size; /*!< Size of the receive buffer */
char *proc_buf; /*!< Pointer to processing buffer, chunks of data from receive buffer and collected here. */
int proc_buf_size; /*!< Size of processing buffer*/
http_callback http_connected_cb; /*!< Pointer to function called once socket connection is established */
http_callback http_process_chunk_cb; /*!< Pointer to function called to process contents of receive buffer, once it is filled up */
http_callback http_disconnected_cb; /*!< Pointer to function called after disconnecting from the socket */
} http_client_data;
#define ESP_ERR_HTTP_BASE 0x40000
#define ESP_ERR_HTTP_DNS_LOOKUP_FAILED (ESP_ERR_HTTP_BASE + 1)
#define ESP_ERR_HTTP_FAILED_TO_ALLOCATE_SOCKET (ESP_ERR_HTTP_BASE + 2)
#define ESP_ERR_HTTP_SOCKET_CONNECT_FAILED (ESP_ERR_HTTP_BASE + 3)
#define ESP_ERR_HTTP_SOCKET_SEND_FAILED (ESP_ERR_HTTP_BASE + 4)
const char* find_response_body(char * response);
void http_client_on_connected(http_client_data *client, http_callback http_connected_cb);
void http_client_on_process_chunk(http_client_data *client, http_callback http_process_chunk_cb);
void http_client_on_disconnected(http_client_data *client, http_callback http_disconnected_cb);
esp_err_t http_client_request(http_client_data *client, const char *web_server, const char *request_string);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -1,2 +0,0 @@
idf_component_register(SRCS "jsmn.c"
INCLUDE_DIRS "include")

View File

@ -1,471 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2010 Serge Zaitsev
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef JSMN_H
#define JSMN_H
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifdef JSMN_STATIC
#define JSMN_API static
#else
#define JSMN_API extern
#endif
/**
* JSON type identifier. Basic types are:
* o Object
* o Array
* o String
* o Other primitive: number, boolean (true/false) or null
*/
typedef enum {
JSMN_UNDEFINED = 0,
JSMN_OBJECT = 1 << 0,
JSMN_ARRAY = 1 << 1,
JSMN_STRING = 1 << 2,
JSMN_PRIMITIVE = 1 << 3
} jsmntype_t;
enum jsmnerr {
/* Not enough tokens were provided */
JSMN_ERROR_NOMEM = -1,
/* Invalid character inside JSON string */
JSMN_ERROR_INVAL = -2,
/* The string is not a full JSON packet, more bytes expected */
JSMN_ERROR_PART = -3
};
/**
* JSON token description.
* type type (object, array, string etc.)
* start start position in JSON data string
* end end position in JSON data string
*/
typedef struct jsmntok {
jsmntype_t type;
int start;
int end;
int size;
#ifdef JSMN_PARENT_LINKS
int parent;
#endif
} jsmntok_t;
/**
* JSON parser. Contains an array of token blocks available. Also stores
* the string being parsed now and current position in that string.
*/
typedef struct jsmn_parser {
unsigned int pos; /* offset in the JSON string */
unsigned int toknext; /* next token to allocate */
int toksuper; /* superior token node, e.g. parent object or array */
} jsmn_parser;
/**
* Create JSON parser over an array of tokens
*/
JSMN_API void jsmn_init(jsmn_parser *parser);
/**
* Run JSON parser. It parses a JSON data string into and array of tokens, each
* describing
* a single JSON object.
*/
JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len,
jsmntok_t *tokens, const unsigned int num_tokens);
#ifndef JSMN_HEADER
/**
* Allocates a fresh unused token from the token pool.
*/
static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, jsmntok_t *tokens,
const size_t num_tokens) {
jsmntok_t *tok;
if (parser->toknext >= num_tokens) {
return NULL;
}
tok = &tokens[parser->toknext++];
tok->start = tok->end = -1;
tok->size = 0;
#ifdef JSMN_PARENT_LINKS
tok->parent = -1;
#endif
return tok;
}
/**
* Fills token type and boundaries.
*/
static void jsmn_fill_token(jsmntok_t *token, const jsmntype_t type,
const int start, const int end) {
token->type = type;
token->start = start;
token->end = end;
token->size = 0;
}
/**
* Fills next available token with JSON primitive.
*/
static int jsmn_parse_primitive(jsmn_parser *parser, const char *js,
const size_t len, jsmntok_t *tokens,
const size_t num_tokens) {
jsmntok_t *token;
int start;
start = parser->pos;
for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
switch (js[parser->pos]) {
#ifndef JSMN_STRICT
/* In strict mode primitive must be followed by "," or "}" or "]" */
case ':':
#endif
case '\t':
case '\r':
case '\n':
case ' ':
case ',':
case ']':
case '}':
goto found;
default:
/* to quiet a warning from gcc*/
break;
}
if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
parser->pos = start;
return JSMN_ERROR_INVAL;
}
}
#ifdef JSMN_STRICT
/* In strict mode primitive must be followed by a comma/object/array */
parser->pos = start;
return JSMN_ERROR_PART;
#endif
found:
if (tokens == NULL) {
parser->pos--;
return 0;
}
token = jsmn_alloc_token(parser, tokens, num_tokens);
if (token == NULL) {
parser->pos = start;
return JSMN_ERROR_NOMEM;
}
jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
#ifdef JSMN_PARENT_LINKS
token->parent = parser->toksuper;
#endif
parser->pos--;
return 0;
}
/**
* Fills next token with JSON string.
*/
static int jsmn_parse_string(jsmn_parser *parser, const char *js,
const size_t len, jsmntok_t *tokens,
const size_t num_tokens) {
jsmntok_t *token;
int start = parser->pos;
/* Skip starting quote */
parser->pos++;
for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
char c = js[parser->pos];
/* Quote: end of string */
if (c == '\"') {
if (tokens == NULL) {
return 0;
}
token = jsmn_alloc_token(parser, tokens, num_tokens);
if (token == NULL) {
parser->pos = start;
return JSMN_ERROR_NOMEM;
}
jsmn_fill_token(token, JSMN_STRING, start + 1, parser->pos);
#ifdef JSMN_PARENT_LINKS
token->parent = parser->toksuper;
#endif
return 0;
}
/* Backslash: Quoted symbol expected */
if (c == '\\' && parser->pos + 1 < len) {
int i;
parser->pos++;
switch (js[parser->pos]) {
/* Allowed escaped symbols */
case '\"':
case '/':
case '\\':
case 'b':
case 'f':
case 'r':
case 'n':
case 't':
break;
/* Allows escaped symbol \uXXXX */
case 'u':
parser->pos++;
for (i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0';
i++) {
/* If it isn't a hex character we have an error */
if (!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */
(js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */
(js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */
parser->pos = start;
return JSMN_ERROR_INVAL;
}
parser->pos++;
}
parser->pos--;
break;
/* Unexpected symbol */
default:
parser->pos = start;
return JSMN_ERROR_INVAL;
}
}
}
parser->pos = start;
return JSMN_ERROR_PART;
}
/**
* Parse JSON string and fill tokens.
*/
JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len,
jsmntok_t *tokens, const unsigned int num_tokens) {
int r;
int i;
jsmntok_t *token;
int count = parser->toknext;
for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
char c;
jsmntype_t type;
c = js[parser->pos];
switch (c) {
case '{':
case '[':
count++;
if (tokens == NULL) {
break;
}
token = jsmn_alloc_token(parser, tokens, num_tokens);
if (token == NULL) {
return JSMN_ERROR_NOMEM;
}
if (parser->toksuper != -1) {
jsmntok_t *t = &tokens[parser->toksuper];
#ifdef JSMN_STRICT
/* In strict mode an object or array can't become a key */
if (t->type == JSMN_OBJECT) {
return JSMN_ERROR_INVAL;
}
#endif
t->size++;
#ifdef JSMN_PARENT_LINKS
token->parent = parser->toksuper;
#endif
}
token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
token->start = parser->pos;
parser->toksuper = parser->toknext - 1;
break;
case '}':
case ']':
if (tokens == NULL) {
break;
}
type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
#ifdef JSMN_PARENT_LINKS
if (parser->toknext < 1) {
return JSMN_ERROR_INVAL;
}
token = &tokens[parser->toknext - 1];
for (;;) {
if (token->start != -1 && token->end == -1) {
if (token->type != type) {
return JSMN_ERROR_INVAL;
}
token->end = parser->pos + 1;
parser->toksuper = token->parent;
break;
}
if (token->parent == -1) {
if (token->type != type || parser->toksuper == -1) {
return JSMN_ERROR_INVAL;
}
break;
}
token = &tokens[token->parent];
}
#else
for (i = parser->toknext - 1; i >= 0; i--) {
token = &tokens[i];
if (token->start != -1 && token->end == -1) {
if (token->type != type) {
return JSMN_ERROR_INVAL;
}
parser->toksuper = -1;
token->end = parser->pos + 1;
break;
}
}
/* Error if unmatched closing bracket */
if (i == -1) {
return JSMN_ERROR_INVAL;
}
for (; i >= 0; i--) {
token = &tokens[i];
if (token->start != -1 && token->end == -1) {
parser->toksuper = i;
break;
}
}
#endif
break;
case '\"':
r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
if (r < 0) {
return r;
}
count++;
if (parser->toksuper != -1 && tokens != NULL) {
tokens[parser->toksuper].size++;
}
break;
case '\t':
case '\r':
case '\n':
case ' ':
break;
case ':':
parser->toksuper = parser->toknext - 1;
break;
case ',':
if (tokens != NULL && parser->toksuper != -1 &&
tokens[parser->toksuper].type != JSMN_ARRAY &&
tokens[parser->toksuper].type != JSMN_OBJECT) {
#ifdef JSMN_PARENT_LINKS
parser->toksuper = tokens[parser->toksuper].parent;
#else
for (i = parser->toknext - 1; i >= 0; i--) {
if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) {
if (tokens[i].start != -1 && tokens[i].end == -1) {
parser->toksuper = i;
break;
}
}
}
#endif
}
break;
#ifdef JSMN_STRICT
/* In strict mode primitives are: numbers and booleans */
case '-':
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case 't':
case 'f':
case 'n':
/* And they must not be keys of the object */
if (tokens != NULL && parser->toksuper != -1) {
const jsmntok_t *t = &tokens[parser->toksuper];
if (t->type == JSMN_OBJECT ||
(t->type == JSMN_STRING && t->size != 0)) {
return JSMN_ERROR_INVAL;
}
}
#else
/* In non-strict mode every unquoted value is a primitive */
default:
#endif
r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
if (r < 0) {
return r;
}
count++;
if (parser->toksuper != -1 && tokens != NULL) {
tokens[parser->toksuper].size++;
}
break;
#ifdef JSMN_STRICT
/* Unexpected char in strict mode */
default:
return JSMN_ERROR_INVAL;
#endif
}
}
if (tokens != NULL) {
for (i = parser->toknext - 1; i >= 0; i--) {
/* Unmatched opened object or array */
if (tokens[i].start != -1 && tokens[i].end == -1) {
return JSMN_ERROR_PART;
}
}
}
return count;
}
/**
* Creates a new parser based over a given buffer with an array of tokens
* available.
*/
JSMN_API void jsmn_init(jsmn_parser *parser) {
parser->pos = 0;
parser->toknext = 0;
parser->toksuper = -1;
}
#endif /* JSMN_HEADER */
#ifdef __cplusplus
}
#endif
#endif /* JSMN_H */

View File

@ -1,7 +0,0 @@
#include <stdio.h>
#include "jsmn.h"
void func(void)
{
}

View File

@ -1,3 +1,3 @@
idf_component_register(SRCS "meteofrance.c"
INCLUDE_DIRS "include"
REQUIRES http jsmn json esp_http_client esp-tls)
REQUIRES json esp_http_client esp-tls)

View File

@ -24,6 +24,7 @@ struct meteoforecast_data{
};
typedef void (*weather_data_callback)(struct meteoforecast_data *datas);
typedef void (*weather_data_start_callback)();
typedef struct {
unsigned int humidity;
@ -31,11 +32,13 @@ typedef struct {
float pressure;
unsigned long retreival_period;
weather_data_callback data_retreived_cb;
weather_data_start_callback data_retreived_cb_start;
} weather_data;
void printftemp(struct forecast_temp *tmp);
void printffd(struct meteoforecast_data *tmp);
void on_weather_data_retrieval(weather_data_callback data_retreived_cb);
void on_weather_data_retrieval_start(weather_data_callback data_retreived_cb);
void initialise_weather_data_retrieval(unsigned long retreival_period);

View File

@ -18,7 +18,6 @@
#include <string.h>
#include "meteofrance.h"
#include "jsmn.h"
#include "cJSON.h"
#include "esp_http_client.h"
#include "esp_tls.h"
@ -206,6 +205,8 @@ static bool process_response_body(const char * body)
static void http_request_task(void *pvParameter)
{
while(1) {
ESP_LOGE(TAG,"Début recup méteo --------------------------");
weather.data_retreived_cb_start(NULL);
char *local_response_buffer = heap_caps_malloc((MAX_HTTP_OUTPUT_BUFFER + 1)*(sizeof(char)),MALLOC_CAP_SPIRAM);
//char local_response_buffer[MAX_HTTP_OUTPUT_BUFFER + 1] = {0};
/**
@ -238,7 +239,7 @@ static void http_request_task(void *pvParameter)
} else {
ESP_LOGE(TAG, "HTTP GET request failed: %s", esp_err_to_name(err));
}
ESP_LOGE(TAG, "%s",local_response_buffer);
//ESP_LOGE(TAG, "%s",local_response_buffer);
bool weather_data_phrased = false;
weather_data_phrased = process_response_body(local_response_buffer);
@ -255,13 +256,17 @@ void on_weather_data_retrieval(weather_data_callback data_retreived_cb)
weather.data_retreived_cb = data_retreived_cb;
}
void on_weather_data_retrieval_start(weather_data_callback data_retreived_cb_start)
{
weather.data_retreived_cb_start = data_retreived_cb_start;
}
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", 10 * 2048, NULL, 5, NULL);
ESP_LOGI(TAG, "HTTP request task started");
}

View File

@ -1,3 +0,0 @@
idf_component_register(SRCS "weather.c"
INCLUDE_DIRS "include"
REQUIRES http jsmn)

View File

@ -1,11 +0,0 @@
menu "Wather data retreival from OpenWeatherMap"
config OPENWEATHERMAP_API_KEY
string "OpenWeatherMap API key"
default "12345678901234567890123456789012"
help
API key obtained from 'https://home.openweathermap.org/' site.
You need to set up a free account on this site first.
endmenu

View File

@ -1,2 +0,0 @@
COMPONENT_ADD_INCLUDEDIRS := .

View File

@ -1,40 +0,0 @@
/*
weather.h - 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.
*/
#ifndef WEATHER_H
#define WEATHER_H
#include "esp_err.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef void (*weather_data_callback)(uint32_t *args);
typedef struct {
unsigned int humidity;
float temperature;
float pressure;
unsigned long retreival_period;
weather_data_callback data_retreived_cb;
} weather_data;
#define ESP_ERR_WEATHER_BASE 0x50000
#define ESP_ERR_WEATHER_RETREIVAL_FAILED (ESP_ERR_WEATHER_BASE + 1)
void on_weather_data_retrieval(weather_data_callback data_retreived_cb);
void initialise_weather_data_retrieval(unsigned long retreival_period);
#ifdef __cplusplus
}
#endif
#endif // WEATHER_H

View File

@ -1,183 +0,0 @@
/*
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");
}

View File

@ -270,8 +270,37 @@ static void _app_button_cb(lv_event_t *e)
}
}
lv_subject_t wifiStatus;
lv_subject_t mqttStatus;
lv_subject_t meteoStatus;
static void app_main_display(void)
{
lv_subject_init_int(&wifiStatus,-1);
lv_subject_init_int(&mqttStatus,-1);
lv_subject_init_int(&meteoStatus,-1);
lv_obj_t *cont_status = lv_obj_create(lv_layer_top());
lv_obj_align(cont_status,LV_ALIGN_TOP_RIGHT,0,0);
lv_obj_set_flex_flow(cont_status, LV_FLEX_FLOW_ROW);
lv_obj_set_height(cont_status,LV_SIZE_CONTENT);
lv_obj_set_width(cont_status,LV_SIZE_CONTENT);
lv_obj_t *meteoR = lv_label_create(cont_status);
lv_label_set_text(meteoR,"Refresh");
lv_label_bind_text(meteoR, &meteoStatus, "Meteo %d");
lv_obj_t *wifi = lv_label_create(cont_status);
lv_label_set_text(wifi,"Wifi Ok");
lv_label_bind_text(wifi, &wifiStatus, "Wifi %d");
lv_obj_t *mqtt = lv_label_create(cont_status);
lv_label_set_text(mqtt,"Mqtt Ok");
lv_label_bind_text(mqtt, &mqttStatus, "Mqtt %d");
lv_obj_t *scr = lv_scr_act();
/* Your LVGL objects code here .... */
@ -453,7 +482,7 @@ static lv_obj_t *weatherDay_fragment_create_obj(lv_fragment_t *self, lv_obj_t *p
lv_image_set_offset_y(img1, -8);
lv_image_set_offset_x(img1, -8);
lv_obj_set_size(img1,40,40);
lv_image_set_src(img1, &p1j);
lv_image_set_src(img1,LV_SYMBOL_DUMMY);
//lv_obj_set_style_border_width(img1,2,0);
//lv_obj_set_style_border_color(img1, lv_palette_main(LV_PALETTE_BLUE_GREY), 0);
lv_obj_align(img1, LV_ALIGN_CENTER, 0, 0);
@ -511,6 +540,9 @@ void draw_ihm()
// keys.clear();
lv_obj_clean(lv_scr_act());
/*Create a Tab view object*/
lv_obj_t * tabview;
tabview = lv_tabview_create(lv_screen_active());
@ -563,17 +595,21 @@ void draw_ihm()
lv_style_set_align(&style_container, LV_ALIGN_BOTTOM_LEFT);
lv_style_set_flex_cross_place(&style_container, LV_FLEX_ALIGN_END);
static lv_style_t no_padding;
lv_style_init(&no_padding);
lv_style_set_pad_all(&no_padding, 0);
static lv_style_t style_lbvValue;
lv_style_init(&style_lbvValue);
lv_obj_t *main = tab1;
lv_obj_add_style(main,&no_padding,0);
lv_style_set_text_font(&style_lbvValue, &lv_font_montserrat_40);
lv_obj_set_flex_flow(tab1, LV_FLEX_FLOW_ROW);
lv_obj_set_style_pad_column(tab1, 1, 0);
lv_obj_set_flex_flow(main, LV_FLEX_FLOW_ROW);
lv_obj_set_style_pad_column(main, 1, 0);
/*Create a container with COLUMN flex direction*/
lv_obj_t *cont_col = lv_obj_create(tab1);
lv_obj_t *cont_col = lv_obj_create(main);
lv_obj_set_style_pad_all(cont_col, 5, 0);
lv_obj_set_size(cont_col, 250, 480);
lv_obj_align(cont_col, LV_ALIGN_TOP_LEFT, 0, 0);
@ -581,7 +617,7 @@ void draw_ihm()
lv_obj_set_flex_align(cont_col,LV_FLEX_ALIGN_CENTER,LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
/*Create a container with COLUMN flex direction*/
lv_obj_t *cont_col2 = lv_obj_create(tab1);
lv_obj_t *cont_col2 = lv_obj_create(main);
lv_obj_set_style_pad_all(cont_col2, 5, 0);
lv_obj_set_flex_align(cont_col2, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
@ -760,6 +796,10 @@ static void wifi_event_handler(void *arg, esp_event_base_t event_base,
}
else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED)
{
if(lvgl_port_lock(0)){
lv_subject_set_int(&wifiStatus,0);
lvgl_port_unlock();
}
if (s_retry_num < EXAMPLE_ESP_MAXIMUM_RETRY)
{
esp_wifi_connect();
@ -774,6 +814,10 @@ static void wifi_event_handler(void *arg, esp_event_base_t event_base,
}
else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP)
{
if(lvgl_port_lock(0)){
lv_subject_set_int(&wifiStatus,1);
lvgl_port_unlock();
}
ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));
s_retry_num = 0;
@ -839,15 +883,28 @@ void wifi_init_sta(void)
{
ESP_LOGI(TAG, "connected to ap SSID:%s password:%s",
EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
if(lvgl_port_lock(0)){
lv_subject_set_int(&wifiStatus,1);
lvgl_port_unlock();
}
}
else if (bits & WIFI_FAIL_BIT)
{
ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s",
EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
if(lvgl_port_lock(0)){
lv_subject_set_int(&wifiStatus,0);
lvgl_port_unlock();
}
}
else
{
ESP_LOGE(TAG, "UNEXPECTED EVENT");
if(lvgl_port_lock(0)){
lv_subject_set_int(&wifiStatus,0);
lvgl_port_unlock();
}
}
}
@ -901,6 +958,10 @@ static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_
switch ((esp_mqtt_event_id_t)event_id)
{
case MQTT_EVENT_CONNECTED:
if(lvgl_port_lock(0)){
lv_subject_set_int(&mqttStatus,1);
lvgl_port_unlock();
}
ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");
msg_id = esp_mqtt_client_publish(client, "/topic/qos1", "data_3", 0, 1, 0);
ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id);
@ -924,6 +985,10 @@ static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_
ESP_LOGI(TAG, "sent unsubscribe successful, msg_id=%d", msg_id);
break;
case MQTT_EVENT_DISCONNECTED:
if(lvgl_port_lock(0)){
lv_subject_set_int(&mqttStatus,0);
lvgl_port_unlock();
}
ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");
break;
@ -1043,6 +1108,15 @@ static void mqtt_app_start(void)
esp_mqtt_client_start(client);
}
void weather_data_retreived_start()
{
ESP_LOGE(TAG,"Début recup méteo");
if(lvgl_port_lock(0)){
lv_subject_set_int(&meteoStatus,1);
lvgl_port_unlock();
}
}
void weather_data_retreived(struct meteoforecast_data datas[3])
{
// struct meteoforecast_data* weather = (meteoforecast_data*) args;
@ -1065,6 +1139,7 @@ void weather_data_retreived(struct meteoforecast_data datas[3])
lv_subject_set_pointer(&tempD1Subj, &datas[0]);
lv_subject_set_pointer(&tempD2Subj, &datas[1]);
lv_subject_set_pointer(&tempD3Subj, &datas[2]);
lv_subject_set_int(&meteoStatus,0);
lvgl_port_unlock();
ESP_LOGE(TAG, "------------------------------------- Fin Set des subjects --------------------------------");
}
@ -1099,8 +1174,9 @@ void app_main(void)
wifi_init_sta();
mqtt_app_start();
initialise_weather_data_retrieval(60000);
on_weather_data_retrieval(weather_data_retreived);
on_weather_data_retrieval_start(weather_data_retreived_start);
initialise_weather_data_retrieval(60000);
ESP_LOGW(TAG, "Weather data retrieval initialized");
/* Show LVGL objects */