1
0

fixed rest on cpp; added batch endpoint

This commit is contained in:
Jose
2025-05-10 07:43:30 +02:00
parent 0ce48c18e2
commit 8dc2787b80
10 changed files with 300 additions and 260 deletions

View File

@@ -24,6 +24,11 @@ public class Constants {
public static final String SENSOR = RAW_API_PREFIX + "/groups/:groupId/devices/:deviceId/sensors/:sensorId";
public static final String SENSOR_VALUES = API_PREFIX + "/groups/:groupId/devices/:deviceId/sensors/:sensorId/values";
public static final String BATCH = API_PREFIX + "/batch";
public static final String ADD_GPS_VALUE = RAW_API_PREFIX + "/groups/:groupId/devices/:deviceId/sensors/:sensorId/gps_values";
public static final String ADD_WEATHER_VALUE = RAW_API_PREFIX + "/groups/:groupId/devices/:deviceId/sensors/:sensorId/weather_values";
public static final String ADD_CO_VALUE = RAW_API_PREFIX + "/groups/:groupId/devices/:deviceId/sensors/:sensorId/co_values";
public static final String ACTUATORS = RAW_API_PREFIX + "/groups/:groupId/devices/:deviceId/actuators";
public static final String ACTUATOR = RAW_API_PREFIX + "/groups/:groupId/devices/:deviceId/actuators/:actuator_id";
public static final String ACTUATOR_STATUS = API_PREFIX + "/groups/:groupId/devices/:deviceId/actuators/:actuator_id/status";
@@ -32,9 +37,6 @@ public class Constants {
public static final String VIEW_POLLUTION_MAP = RAW_API_PREFIX + "/v_pollution_map";
public static final String VIEW_SENSOR_HISTORY = RAW_API_PREFIX + "/v_sensor_history_by_device";
public static final String VIEW_SENSOR_VALUES = RAW_API_PREFIX + "/v_sensor_values";
public static final String VIEW_CO_BY_DEVICE = RAW_API_PREFIX + "/v_co_by_device";
public static final String VIEW_GPS_BY_DEVICE = RAW_API_PREFIX + "/v_gps_by_device";
public static final String VIEW_WEATHER_BY_DEVICE = RAW_API_PREFIX + "/v_weather_by_device";
private Constants() {
throw new AssertionError("Utility class cannot be instantiated.");

View File

@@ -4,7 +4,7 @@ import java.util.Map;
import com.google.gson.Gson;
import io.vertx.core.Promise;
import io.vertx.core.Future;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.client.HttpRequest;
@@ -12,128 +12,46 @@ import io.vertx.ext.web.client.WebClient;
public class RestClientUtil {
public WebClient client;
private Gson gson;
private final WebClient client;
private final Gson gson;
public RestClientUtil(WebClient client) {
gson = new Gson();
this.client = client;
}
public RestClientUtil(WebClient client) {
this.client = client;
this.gson = new Gson();
}
/**
* Get request utility
*
* @param <T> Type of result enveloped in JSON response
* @param port Port
* @param host Host address
* @param resource URI where resource is provided
* @param classType Type of result enveloped in JSON response
* @param promise Promise to be executed on call finish
*/
public <T> void getRequest(Integer port, String host, String resource, Class<T> classType, Promise<T> promise) {
client.getAbs(host + ":" + port + resource).send(elem -> {
if (elem.succeeded()) {
promise.complete(gson.fromJson(elem.result().bodyAsString(), classType));
} else {
promise.fail(elem.cause());
}
});
public <T> Future<T> getRequest(int port, String host, String resource, Class<T> classType) {
return client.getAbs(host + ":" + port + resource)
.send()
.map(response -> gson.fromJson(response.bodyAsString(), classType));
}
}
public <T> Future<T> getRequestWithParams(int port, String host, String resource, Class<T> classType,
Map<String, String> params) {
HttpRequest<Buffer> httpRequest = client.getAbs(host + ":" + port + "/" + resource);
params.forEach(httpRequest::addQueryParam);
/**
* Get request utility
*
* @param <T> Type of result enveloped in JSON response
* @param port Port
* @param host Host address
* @param resource URI where resource is provided
* @param classType Type of result enveloped in JSON response
* @param promise Promise to be executed on call finish
* @param params Map with key-value entries for call parameters
*/
public <T> void getRequestWithParams(Integer port, String host, String resource, Class<T> classType,
Promise<T> promise, Map<String, String> params) {
HttpRequest<Buffer> httpRequest = client.getAbs(host + ":" + port + "/" + resource);
return httpRequest.send()
.map(response -> gson.fromJson(response.bodyAsString(), classType));
}
params.forEach((key, value) -> {
httpRequest.addQueryParam(key, value);
});
public <B, T> Future<T> postRequest(int port, String host, String resource, B body, Class<T> classType) {
JsonObject jsonBody = new JsonObject(gson.toJson(body));
return client.postAbs(host + ":" + port + "/" + resource)
.sendJsonObject(jsonBody)
.map(response -> gson.fromJson(response.bodyAsString(), classType));
}
httpRequest.send(elem -> {
if (elem.succeeded()) {
promise.complete(gson.fromJson(elem.result().bodyAsString(), classType));
} else {
promise.fail(elem.cause());
}
});
public <B, T> Future<T> putRequest(int port, String host, String resource, B body, Class<T> classType) {
JsonObject jsonBody = new JsonObject(gson.toJson(body));
return client.putAbs(host + ":" + port + "/" + resource)
.sendJsonObject(jsonBody)
.map(response -> gson.fromJson(response.bodyAsString(), classType));
}
}
/**
* Post request utility
*
* @param <B> Type of body enveloped in JSON request
* @param <T> Type of result enveloped in JSON response
* @param port Port
* @param host Host address
* @param resource URI where resource is provided
* @param classType Type of result enveloped in JSON response
* @param promise Promise to be executed on call finish
*/
public <B, T> void postRequest(Integer port, String host, String resource, Object body, Class<T> classType,
Promise<T> promise) {
JsonObject jsonBody = new JsonObject(gson.toJson(body));
client.postAbs(host + ":" + port + "/" + resource).sendJsonObject(jsonBody, elem -> {
if (elem.succeeded()) {
Gson gson = new Gson();
promise.complete(gson.fromJson(elem.result().bodyAsString(), classType));
} else {
promise.fail(elem.cause());
}
});
}
/**
* Put request utility
*
* @param <B> Type of body enveloped in JSON request
* @param <T> Type of result enveloped in JSON response
* @param port Port
* @param host Host address
* @param resource URI where resource is provided
* @param classType Type of result enveloped in JSON response
* @param promise Promise to be executed on call finish
*/
public <B, T> void putRequest(Integer port, String host, String resource, Object body, Class<T> classType,
Promise<T> promise) {
JsonObject jsonBody = new JsonObject(gson.toJson(body));
client.putAbs(host + ":" + port + "/" + resource).sendJsonObject(jsonBody, elem -> {
if (elem.succeeded()) {
Gson gson = new Gson();
promise.complete(gson.fromJson(elem.result().bodyAsString(), classType));
} else {
promise.fail(elem.cause());
}
});
}
/**
* Delete request utility
*
* @param port Port
* @param host Host address
* @param resource URI where resource is provided
* @param promise Promise to be executed on call finish
*/
public void deleteRequest(Integer port, String host, String resource, Promise<String> promise) {
client.deleteAbs(host + ":" + port + "/" + resource).send(elem -> {
if (elem.succeeded()) {
promise.complete(elem.result().bodyAsString());
} else {
promise.fail(elem.cause());
}
});
}
public Future<String> deleteRequest(int port, String host, String resource) {
return client.deleteAbs(host + ":" + port + "/" + resource)
.send()
.map(response -> response.bodyAsString());
}
}

View File

@@ -36,13 +36,16 @@ import net.miarma.contaminus.db.DatabaseManager;
import net.miarma.contaminus.db.DatabaseProvider;
import net.miarma.contaminus.db.QueryBuilder;
import net.miarma.contaminus.entities.Actuator;
import net.miarma.contaminus.entities.COValue;
import net.miarma.contaminus.entities.Device;
import net.miarma.contaminus.entities.GpsValue;
import net.miarma.contaminus.entities.Group;
import net.miarma.contaminus.entities.Sensor;
import net.miarma.contaminus.entities.ViewLatestValues;
import net.miarma.contaminus.entities.ViewPollutionMap;
import net.miarma.contaminus.entities.ViewSensorHistory;
import net.miarma.contaminus.entities.ViewSensorValue;
import net.miarma.contaminus.entities.WeatherValue;
/*
@@ -119,6 +122,10 @@ public class DataLayerAPIVerticle extends AbstractVerticle {
router.route(HttpMethod.GET, Constants.SENSOR).handler(this::getSensorById);
router.route(HttpMethod.POST, Constants.SENSORS).handler(this::addSensor);
router.route(HttpMethod.PUT, Constants.SENSOR).handler(this::updateSensor);
router.route(HttpMethod.POST, Constants.ADD_GPS_VALUE).handler(this::addGpsValue);
router.route(HttpMethod.POST, Constants.ADD_WEATHER_VALUE).handler(this::addWeatherValue);
router.route(HttpMethod.POST, Constants.ADD_CO_VALUE).handler(this::addCoValue);
// Actuator Routes
router.route(HttpMethod.GET, Constants.ACTUATORS).handler(this::getAllActuators);
@@ -185,7 +192,7 @@ public class DataLayerAPIVerticle extends AbstractVerticle {
.onSuccess(result -> {
context.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(gson.toJson(SingleJsonResponse.of("Group added successfully")));
.end(gson.toJson(result, Group.class));
})
.onFailure(err -> {
context.fail(500, err);
@@ -200,7 +207,7 @@ public class DataLayerAPIVerticle extends AbstractVerticle {
.onSuccess(result -> {
context.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(gson.toJson(SingleJsonResponse.of("Group updated successfully")));
.end(gson.toJson(result, Group.class));
})
.onFailure(err -> {
context.fail(500, err);
@@ -248,7 +255,7 @@ public class DataLayerAPIVerticle extends AbstractVerticle {
.onSuccess(result -> {
context.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(gson.toJson(SingleJsonResponse.of("Device added successfully")));
.end(gson.toJson(result, Device.class));
})
.onFailure(err -> {
context.fail(500, err);
@@ -263,7 +270,7 @@ public class DataLayerAPIVerticle extends AbstractVerticle {
.onSuccess(result -> {
context.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(gson.toJson(SingleJsonResponse.of("Device updated successfully")));
.end(gson.toJson(result, Device.class));
})
.onFailure(err -> {
context.fail(500, err);
@@ -319,7 +326,7 @@ public class DataLayerAPIVerticle extends AbstractVerticle {
.onSuccess(result -> {
context.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(gson.toJson(SingleJsonResponse.of("Sensor added successfully")));
.end(gson.toJson(result, Sensor.class));
})
.onFailure(err -> {
context.fail(500, err);
@@ -334,13 +341,61 @@ public class DataLayerAPIVerticle extends AbstractVerticle {
.onSuccess(result -> {
context.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(gson.toJson(SingleJsonResponse.of("Sensor updated successfully")));
.end(gson.toJson(result, Sensor.class));
})
.onFailure(err -> {
context.fail(500, err);
});
}
private void addGpsValue(RoutingContext context) {
JsonObject body = context.body().asJsonObject();
GpsValue gpsValue = gson.fromJson(body.toString(), GpsValue.class);
gpsValueDAO.insert(gpsValue)
.onSuccess(result -> {
context.response()
.setStatusCode(201)
.putHeader("Content-Type", "application/json")
.end(gson.toJson(result, GpsValue.class));
})
.onFailure(err -> {
context.fail(500, err);
});
}
private void addWeatherValue(RoutingContext context) {
JsonObject body = context.body().asJsonObject();
WeatherValue weatherValue = gson.fromJson(body.toString(), WeatherValue.class);
weatherValueDAO.insert(weatherValue)
.onSuccess(result -> {
context.response()
.setStatusCode(201)
.putHeader("Content-Type", "application/json")
.end(gson.toJson(result, WeatherValue.class));
})
.onFailure(err -> {
context.fail(500, err);
});
}
private void addCoValue(RoutingContext context) {
JsonObject body = context.body().asJsonObject();
COValue coValue = gson.fromJson(body.toString(), COValue.class);
coValueDAO.insert(coValue)
.onSuccess(result -> {
context.response()
.setStatusCode(201)
.putHeader("Content-Type", "application/json")
.end(gson.toJson(result, COValue.class));
})
.onFailure(err -> {
context.fail(500, err);
});
}
private void getAllActuators(RoutingContext context) {
Integer groupId = Integer.parseInt(context.request().getParam("groupId"));
String deviceId = context.request().getParam("deviceId");
@@ -390,7 +445,7 @@ public class DataLayerAPIVerticle extends AbstractVerticle {
.onSuccess(result -> {
context.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(gson.toJson(SingleJsonResponse.of("Actuator added successfully")));
.end(gson.toJson(result, Actuator.class));
})
.onFailure(err -> {
context.fail(500, err);
@@ -405,7 +460,7 @@ public class DataLayerAPIVerticle extends AbstractVerticle {
.onSuccess(result -> {
context.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(gson.toJson(SingleJsonResponse.of("Actuator updated successfully")));
.end(gson.toJson(result, Actuator.class));
})
.onFailure(err -> {
context.fail(500, err);

View File

@@ -4,7 +4,6 @@ import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Stream;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
@@ -13,6 +12,7 @@ import io.vertx.core.AbstractVerticle;
import io.vertx.core.Promise;
import io.vertx.core.Vertx;
import io.vertx.core.http.HttpMethod;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.client.WebClient;
@@ -21,10 +21,13 @@ import io.vertx.ext.web.handler.BodyHandler;
import io.vertx.ext.web.handler.CorsHandler;
import net.miarma.contaminus.common.ConfigManager;
import net.miarma.contaminus.common.Constants;
import net.miarma.contaminus.entities.COValue;
import net.miarma.contaminus.entities.GpsValue;
import net.miarma.contaminus.entities.ViewLatestValues;
import net.miarma.contaminus.entities.ViewPollutionMap;
import net.miarma.contaminus.entities.ViewSensorHistory;
import net.miarma.contaminus.entities.ViewSensorValue;
import net.miarma.contaminus.entities.WeatherValue;
import net.miarma.contaminus.util.RestClientUtil;
public class LogicLayerAPIVerticle extends AbstractVerticle {
@@ -55,6 +58,7 @@ public class LogicLayerAPIVerticle extends AbstractVerticle {
router.route().handler(BodyHandler.create());
router.route(HttpMethod.POST, Constants.BATCH).handler(this::addBatch);
router.route(HttpMethod.GET, Constants.LATEST_VALUES).handler(this::getDeviceLatestValues);
router.route(HttpMethod.GET, Constants.POLLUTION_MAP).handler(this::getDevicePollutionMap);
router.route(HttpMethod.GET, Constants.HISTORY).handler(this::getDeviceHistory);
@@ -68,93 +72,100 @@ public class LogicLayerAPIVerticle extends AbstractVerticle {
}
private void getDeviceLatestValues(RoutingContext context) {
String deviceId = context.request().getParam("deviceId");
Promise<ViewLatestValues[]> resultList = Promise.promise();
resultList.future().onComplete(complete -> {
if (complete.succeeded()) {
List<ViewLatestValues> aux = Stream.of(complete.result())
.filter(elem -> deviceId.equals(elem.getDeviceId()))
.toList();
context.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(gson.toJson(aux));
} else {
context.fail(500, complete.cause());
}
});
this.restClient.getRequest(configManager.getDataApiPort(), "http://" + configManager.getHost(),
Constants.VIEW_LATEST_VALUES, ViewLatestValues[].class, resultList);
String deviceId = context.request().getParam("deviceId");
restClient.getRequest(configManager.getDataApiPort(), "http://" + configManager.getHost(),
Constants.VIEW_LATEST_VALUES, ViewLatestValues[].class)
.onSuccess(result -> {
List<ViewLatestValues> aux = Arrays.stream(result)
.filter(elem -> deviceId.equals(elem.getDeviceId()))
.toList();
context.response().putHeader("content-type", "application/json; charset=utf-8").end(gson.toJson(aux));
})
.onFailure(err -> context.fail(500, err));
}
private void getDevicePollutionMap(RoutingContext context) {
String deviceId = context.request().getParam("deviceId");
Promise<ViewPollutionMap[]> resultList = Promise.promise();
resultList.future().onComplete(complete -> {
if (complete.succeeded()) {
List<ViewPollutionMap> aux = Arrays.asList(complete.result()).stream()
.filter(elem -> deviceId.equals(elem.getDeviceId()))
.toList();
context.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(gson.toJson(aux));
} else {
context.fail(500, complete.cause());
}
});
this.restClient.getRequest(configManager.getDataApiPort(), "http://" + configManager.getHost(),
Constants.VIEW_POLLUTION_MAP, ViewPollutionMap[].class, resultList);
}
String deviceId = context.request().getParam("deviceId");
restClient.getRequest(configManager.getDataApiPort(), "http://" + configManager.getHost(),
Constants.VIEW_POLLUTION_MAP, ViewPollutionMap[].class)
.onSuccess(result -> {
List<ViewPollutionMap> aux = Arrays.asList(result).stream()
.filter(elem -> deviceId.equals(elem.getDeviceId()))
.toList();
context.response().putHeader("content-type", "application/json; charset=utf-8").end(gson.toJson(aux));
})
.onFailure(err -> context.fail(500, err));
}
private void getDeviceHistory(RoutingContext context) {
String deviceId = context.request().getParam("deviceId");
Promise<ViewSensorHistory[]> resultList = Promise.promise();
resultList.future().onComplete(complete -> {
if (complete.succeeded()) {
List<ViewSensorHistory> aux = Arrays.asList(complete.result()).stream()
.filter(elem -> deviceId.equals(elem.getDeviceId()))
.toList();
context.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(gson.toJson(aux));
} else {
context.fail(500, complete.cause());
}
});
this.restClient.getRequest(configManager.getDataApiPort(), "http://" + configManager.getHost(),
Constants.VIEW_SENSOR_HISTORY, ViewSensorHistory[].class, resultList);
String deviceId = context.request().getParam("deviceId");
restClient.getRequest(configManager.getDataApiPort(), "http://" + configManager.getHost(),
Constants.VIEW_SENSOR_HISTORY, ViewSensorHistory[].class)
.onSuccess(result -> {
List<ViewSensorHistory> aux = Arrays.asList(result).stream()
.filter(elem -> deviceId.equals(elem.getDeviceId()))
.toList();
context.response().putHeader("content-type", "application/json; charset=utf-8").end(gson.toJson(aux));
})
.onFailure(err -> context.fail(500, err));
}
private void getSensorValues(RoutingContext context) {
Integer sensorId = Integer.parseInt(context.request().getParam("sensorId"));
int sensorId = Integer.parseInt(context.request().getParam("sensorId"));
restClient.getRequest(configManager.getDataApiPort(), "http://" + configManager.getHost(),
Constants.VIEW_SENSOR_VALUES, ViewSensorValue[].class)
.onSuccess(result -> {
List<ViewSensorValue> aux = Arrays.asList(result).stream()
.filter(val -> val.getSensorId() == sensorId)
.toList();
context.response().putHeader("content-type", "application/json; charset=utf-8").end(gson.toJson(aux));
})
.onFailure(err -> context.fail(500, err));
}
Promise<ViewSensorValue[]> resultList = Promise.promise();
private void addBatch(RoutingContext context) {
JsonObject body = context.body().asJsonObject();
if (body == null) {
context.response().setStatusCode(400).end("Missing JSON body");
return;
}
resultList.future().onComplete(complete -> {
if (complete.succeeded()) {
List<ViewSensorValue> aux = Arrays.asList(complete.result()).stream()
.filter(val -> val.getSensorId() == sensorId)
.toList();
String groupId = body.getString("groupId");
String deviceId = body.getString("deviceId");
JsonObject gps = body.getJsonObject("gps");
JsonObject weather = body.getJsonObject("weather");
JsonObject co = body.getJsonObject("co");
if (deviceId == null || gps == null || weather == null || co == null) {
context.response().setStatusCode(400).end("Missing required fields");
return;
}
GpsValue gpsValue = gson.fromJson(gps.toString(), GpsValue.class);
WeatherValue weatherValue = gson.fromJson(weather.toString(), WeatherValue.class);
COValue coValue = gson.fromJson(co.toString(), COValue.class);
gpsValue.setDeviceId(deviceId);
weatherValue.setDeviceId(deviceId);
coValue.setDeviceId(deviceId);
String host = "http://" + configManager.getHost();
int port = configManager.getDataApiPort();
String gpsPath = Constants.ADD_GPS_VALUE.replace(":groupId", groupId).replace(":deviceId", deviceId);
String weatherPath = Constants.ADD_WEATHER_VALUE.replace(":groupId", groupId).replace(":deviceId", deviceId);
String coPath = Constants.ADD_CO_VALUE.replace(":groupId", groupId).replace(":deviceId", deviceId);
restClient.postRequest(port, host, gpsPath, gpsValue, GpsValue.class)
.compose(_ -> restClient.postRequest(port, host, weatherPath, weatherValue, WeatherValue.class))
.compose(_ -> restClient.postRequest(port, host, coPath, coValue, COValue.class))
.onSuccess(_ -> {
context.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(gson.toJson(aux));
} else {
context.fail(500, complete.cause());
}
});
this.restClient.getRequest(configManager.getDataApiPort(), "http://" + configManager.getHost(),
Constants.VIEW_SENSOR_VALUES, ViewSensorValue[].class, resultList);
}
.setStatusCode(201)
.putHeader("Content-Type", "application/json")
.end(new JsonObject().put("status", "success").put("inserted", 3).encode());
})
.onFailure(err -> context.fail(500, err));
}
}

View File

@@ -7,15 +7,14 @@
#include "GPS.hpp"
String serializeSensorValue(
int sensorId,
int groupId,
const String &deviceId,
const String &sensorType,
const String &unit,
int sensorStatus,
int gpsSensorId,
int weatherSensorId,
int coSensorId,
const BME280Data_t &bme,
const MQ7Data_t &mq7,
const GPSData_t &gps,
long timestamp);
const GPSData_t &gps);
String serializeActuatorStatus(
int actuatorId,

View File

@@ -2,5 +2,5 @@
#include <HTTPClient.h>
void getRequest(HTTPClient &httpClient, const String url, String &response);
void postRequest(HTTPClient &httpClient, const String url, const String &payload, String &response);
void getRequest(const String url, String &response);
void postRequest(const String url, const String &payload, String &response);

View File

@@ -4,6 +4,8 @@
#define REST_PORT 443
#define MQTT_PORT 1883
#define GROUP_ID 1
#define MQ7_ID 1
#define BME280_ID 2
#define GPS_ID 3
@@ -48,4 +50,5 @@ void readBME280();
void readGPS();
void writeMatrix(const char *message);
void printAllData();
void sendSensorData();
uint32_t getChipID();

View File

@@ -1,30 +1,34 @@
#include "JsonTools.hpp"
String serializeSensorValue(
int sensorId,
int groupId,
const String &deviceId,
const String &sensorType,
const String &unit,
int sensorStatus,
int gpsSensorId,
int weatherSensorId,
int coSensorId,
const BME280Data_t &bme,
const MQ7Data_t &mq7,
const GPSData_t &gps,
long timestamp)
const GPSData_t &gps)
{
DynamicJsonDocument doc(1024);
doc["sensorId"] = sensorId;
doc["groupId"] = groupId;
doc["deviceId"] = deviceId;
doc["sensorType"] = sensorType;
doc["unit"] = unit;
doc["sensorStatus"] = sensorStatus;
doc["temperature"] = bme.temperature;
doc["humidity"] = bme.humidity;
doc["pressure"] = bme.pressure;
doc["carbonMonoxide"] = mq7.co;
doc["lat"] = gps.lat;
doc["lon"] = gps.lon;
doc["timestamp"] = timestamp;
JsonObject gpsObj = doc.createNestedObject("gps");
gpsObj["sensorId"] = gpsSensorId;
gpsObj["lat"] = gps.lat;
gpsObj["lon"] = gps.lon;
JsonObject weather = doc.createNestedObject("weather");
weather["sensorId"] = weatherSensorId;
weather["temperature"] = bme.temperature;
weather["humidity"] = bme.humidity;
weather["pressure"] = bme.pressure;
JsonObject co = doc.createNestedObject("co");
co["sensorId"] = coSensorId;
co["value"] = mq7.co;
String output;
serializeJson(doc, output);
@@ -68,6 +72,7 @@ void deserializeSensorValue(HTTPClient &http, int httpResponseCode)
Serial.print("HTTP Response code: ");
Serial.println(httpResponseCode);
String responseJson = http.getString();
DynamicJsonDocument doc(ESP.getMaxAllocHeap());
DeserializationError error = deserializeJson(doc, responseJson);
@@ -78,26 +83,35 @@ void deserializeSensorValue(HTTPClient &http, int httpResponseCode)
return;
}
JsonArray array = doc.as<JsonArray>();
for (JsonObject sensor : array)
{
int sensorId = sensor["sensorId"];
String deviceId = sensor["deviceId"];
String sensorType = sensor["sensorType"];
String unit = sensor["unit"];
int sensorStatus = sensor["sensorStatus"];
float temperature = sensor["temperature"];
float humidity = sensor["humidity"];
float carbonMonoxide = sensor["carbonMonoxide"];
float lat = sensor["lat"];
float lon = sensor["lon"];
long timestamp = sensor["timestamp"];
String groupId = doc["groupId"];
String deviceId = doc["deviceId"];
Serial.println("Sensor deserialized:");
Serial.printf(" ID: %d\n Device: %s\n Type: %s\n Unit: %s\n Status: %d\n Temp: %.2f\n Hum: %.2f\n CO: %.2f\n Lat: %.6f\n Lon: %.6f\n Time: %ld\n\n",
sensorId, deviceId.c_str(), sensorType.c_str(), unit.c_str(), sensorStatus,
temperature, humidity, carbonMonoxide, lat, lon, timestamp);
}
JsonObject gps = doc["gps"];
int gpsId = gps["sensorId"];
float lat = gps["lat"];
float lon = gps["lon"];
JsonObject weather = doc["weather"];
int weatherId = weather["sensorId"];
float temp = weather["temperature"];
float hum = weather["humidity"];
float pres = weather["pressure"];
JsonObject co = doc["co"];
int coId = co["sensorId"];
float coVal = co["value"];
Serial.println("🛰 GPS:");
Serial.printf(" Sensor ID: %d\n Lat: %.6f Lon: %.6f\n", gpsId, lat, lon);
Serial.println("🌤 Weather:");
Serial.printf(" Sensor ID: %d\n Temp: %.2f°C Hum: %.2f%% Pressure: %.2f hPa\n", weatherId, temp, hum, pres);
Serial.println("🧪 CO:");
Serial.printf(" Sensor ID: %d\n CO: %.2f ppm\n", coId, coVal);
Serial.printf("🧾 Group ID: %s\n", groupId.c_str());
Serial.printf("🧾 Device ID: %s\n", deviceId.c_str());
}
else
{

View File

@@ -17,7 +17,7 @@ void getRequest(const String url, String &response)
httpClient.end();
}
void postRequest(const String url, String &payload, String &response)
void postRequest(const String url, const String &payload, String &response)
{
httpClient.begin(url);
httpClient.addHeader("Content-Type", "application/json");

View File

@@ -9,6 +9,7 @@ TaskTimer matrixTimer{0, 25};
TaskTimer globalTimer{0, 60000};
extern HTTPClient httpClient;
String response;
extern MD_Parola display;
MQ7Data_t mq7Data;
@@ -59,6 +60,8 @@ void loop()
printAllData();
#endif
sendSensorData();
globalTimer.lastRun = now;
}
}
@@ -136,6 +139,41 @@ void printAllData()
Serial.println(mq7Data.threshold);
}
void sendSensorData()
{
const String deviceId = String(DEVICE_ID, HEX);
// Validaciones básicas (puedes añadir más si quieres)
bool gpsValid = gpsData.lat != 0.0f && gpsData.lon != 0.0f;
bool weatherValid = bme280Data.temperature != 0.0f &&
bme280Data.humidity != 0.0f &&
bme280Data.pressure != 0.0f;
bool coValid = mq7Data.co >= 0.0f;
if (!gpsValid || !weatherValid || !coValid)
{
#ifdef DEBUG
Serial.println("❌ Datos inválidos. No se envía el batch.");
#endif
return;
}
String json = serializeSensorValue(GROUP_ID, deviceId,
GPS_ID, BME280_ID, MQ7_ID,
bme280Data, mq7Data, gpsData);
#ifdef DEBUG
Serial.println("📤 Enviando datos al servidor...");
#endif
postRequest(String(SERVER_IP) + "/batch", json, response);
#ifdef DEBUG
Serial.println("📬 Respuesta del servidor:");
Serial.println(response);
#endif
}
uint32_t getChipID()
{
uint32_t chipId = 0;