Commit 291f5c8d authored by Nelso Jost's avatar Nelso Jost
Browse files

ADD send to server basic functionality

parent 637df50d
......@@ -36,7 +36,7 @@ String read_DHT22_AH()
Adafruit_BMP085 bmp;
bool is_bmp085_connected=false;
String read_BMP085_PRESSURE()
String read_BMP180_PRESS()
{
if (is_bmp085_connected)
{
......@@ -44,7 +44,19 @@ String read_BMP085_PRESSURE()
}
else
{
return String("<bmp085_not_found>");
return String("<bmp180_not_found>");
}
}
String read_BMP180_TEMP()
{
if (is_bmp085_connected)
{
return String(bmp.readTemperature());
}
else
{
return String("<bmp180_not_found>");
}
}
......
......@@ -11,7 +11,8 @@ String read_DHT22_TEMP();
String read_DHT22_AH();
#include "Adafruit_BMP085.h"
String read_BMP085_PRESSURE();
String read_BMP180_PRESS();
String read_BMP180_TEMP();
#include "RTClib.h"
String read_RTC_DS1307();
......@@ -20,20 +21,22 @@ String set_time_from_csv(String s);
void mysensors_setup();
#define __SENSOR_COUNT 5
#define __SENSOR_COUNT 6
#define __SENSOR_NICKNAMES {"l", "p", "t", "ah", "dt"}
#define __SENSOR_NICKNAMES {"l", "p", "T", " t", "ah", "dt"}
#define __SENSOR_NAMES {"LDR", \
"BMP085_PRESSURE", \
"BMP180_PRESS", \
"BMP180_TEMP", \
"DHT22_TEMP", \
"DHT22_AH", \
"RTC_DS1307"}
#define __FP_READ_SENSOR {&read_LDR, \
&read_BMP085_PRESSURE, \
&read_DHT22_TEMP, \
&read_DHT22_AH, \
#define __FP_READ_SENSOR {&read_LDR, \
&read_BMP180_PRESS, \
&read_BMP180_TEMP, \
&read_DHT22_TEMP, \
&read_DHT22_AH, \
&read_RTC_DS1307}
#endif
This diff is collapsed.
......@@ -5,6 +5,7 @@
*---------------------------------------------------------------------------*/
#include <Arduino.h>
#include <SoftwareSerial.h>
#include "FS.h"
#include "repl.h"
#include "repl_gpio.h"
......@@ -15,8 +16,10 @@
#include "nodemcu_pinout.h"
#include "meteorologger.h"
#define BAUDRATE 115200
#define DATALOG_FILENAME "datalog.txt"
#define DATALOG_FILENAME "DATALOG.TXT"
#define CONFIG_FILENAME "LOGGER.CONF"
#define ARDUINO_TX D1 // (soft RX)
#define ARDUINO_RX D2 // (soft TX)
......@@ -39,14 +42,16 @@ REPL repl(commands);
SoftwareSerial arduinoSerial(ARDUINO_TX, ARDUINO_RX); // RX, TX
Meteorologger meteorologger(&arduinoSerial, &repl, String(DATALOG_FILENAME));
Meteorologger meteorologger(&repl, &arduinoSerial,
String(DATALOG_FILENAME),
String(CONFIG_FILENAME));
void setup()
{
Serial.begin(BAUDRATE);
arduinoSerial.begin(ARDUINO_BAUDRATE);
sdcard_begin(CS_PIN);
meteorologger.begin();
}
......@@ -59,7 +64,7 @@ void loop()
void REPL_ARDUINO(REPL * repl)
{
meteorologger.sendCommand(repl->get_arg(1));
meteorologger.send_arduino_command(repl->get_arg(1));
}
......@@ -71,6 +76,17 @@ void REPL_METEOROLOG(REPL * repl)
void REPL_TAILDATALOG(REPL * repl)
{
sdcard_tail(meteorologger.datalog_filename);
meteorologger.taildatalog();
}
void REPL_SETBOARD(REPL * repl)
{
meteorologger.set_board(repl->get_arg(1).toInt(), repl->get_arg(2));
}
void REPL_SHOWCONFIG(REPL * repl)
{
meteorologger.print_config();
}
#include <Arduino.h>
#include <SoftwareSerial.h>
#include "FS.h"
#include "ArduinoJson.h"
#include "meteorologger.h"
#include "repl.h"
#include "sdcard.h"
#include "wifi.h"
DynamicJsonBuffer jsonBuffer(1000);
Meteorologger::Meteorologger(SoftwareSerial * arduinoSerial,
REPL * repl,
String datalog_filename)
Meteorologger::Meteorologger(REPL * repl,
SoftwareSerial * arduinoSerial,
String datalog_filename,
String config_filename)
{
this->arduinoSerial = arduinoSerial;
this->repl = repl;
this->datalog_filename = datalog_filename;
this->config_filename = config_filename;
}
void Meteorologger::begin(void)
{
pinMode(this->pin_notify, OUTPUT);
SPIFFS.begin();
this->load_config();
this->activate(this->interval_minutes, this->arduino_command);
}
void Meteorologger::update(void)
{
if (!this->arduinoSerial->available()) return;
if (!this->arduino_waiting)
{
this->arduinoSerial->readString();
return;
}
this->arduino_waiting = false;
this->repl->log("\n[DEBUG] Reading arduino serial response..");
this->response = this->arduinoSerial->readString();
this->response.trim();
this->repl->log("\n[INFO] Recieved: '" + this->response + "' (" +
String(this->response.length()) + " bytes)");
this->arduino_response = this->arduinoSerial->readString();
this->arduino_response.trim();
this->repl->log("\n[INFO] Recieved: '" + this->arduino_response + "' (" +
String(this->arduino_response.length()) + " bytes)");
if (this->interval == 0) return;
if (this->interval_minutes == 0) return;
if (!sdcard_write(this->datalog_filename, this->response + "\n"))
String json=this->generate_json();
this->repl->log("\n[DEBUG] --- JSON created ---\n" + json);
if (!this->server_upload(json))
{
this->status_level = STATUS_LEVEL_ERROR_SD;
return;
this->status_level = STATUS_ERROR_SERVER;
}
this->status_level = STATUS_LEVEL_OK;
String datalog_line=this->arduino_response + "," + String(
(this->status_level == STATUS_ERROR_SERVER)? 0:1);
if (!sdcard_write(this->datalog_filename, datalog_line + "\n"))
{
if (this->status_level == STATUS_ERROR_SERVER)
{
this->status_level = STATUS_ERROR_SD_SERVER;
}
else
{
this->status_level = STATUS_ERROR_SD_ONLY;
}
return;
}
this->status_level = STATUS_OK;
}
void Meteorologger::activate(float interval, String command)
{
this->interval = interval;
this->interval_minutes = interval;
if (interval == 0)
{
this->ticker_meteorolog.detach();
this->ticker_notify_status.detach();
this->repl->log("\n[INFO] Deactivated meteorolog timer.");
digitalWrite(this->pin_notify, 1);
this->notify_off();
}
else
{
this->command = command;
this->arduino_command = command;
this->ticker_meteorolog.attach(interval * 60,
this->meteorolog_callback, this);
this->notify_on();
this->repl->log("\n[INFO] Activated meteorolog timer with " +
String(interval) + " min (" + String(interval*60) +
" sec) interval.");
sdcard_write(this->datalog_filename, "# --- [START] --- command='"
+ command + "' --- interval_mins=" + String(interval)
+ "\n");
this->sendCommand(command);
sdcard_write(this->datalog_filename,
"# --- [START] --- arduino_command='" + command +
"' --- interval_minutes=" + String(interval) + "\n");
this->send_arduino_command(command);
}
this->save_config();
}
void Meteorologger::sendCommand(String command)
void Meteorologger::send_arduino_command(String command)
{
this->response="";
this->command = command;
this->repl->log("\n[DEBUG] Sending command '" + this->command +
"' to arduino.");
this->arduinoSerial->print(this->command);
this->arduino_response="";
this->arduino_command = command;
this->repl->log("\n[DEBUG] Sending command '" + this->arduino_command +
"' to Arduino.");
this->arduinoSerial->print(this->arduino_command);
this->arduino_waiting = true;
this->ticker_arduino_threshold.attach(this->arduino_threshold,
this->arduino_threshold_callback, this);
}
......@@ -80,19 +116,19 @@ void Meteorologger::sendCommand(String command)
void Meteorologger::meteorolog_callback(Meteorologger * m)
{
m->sendCommand(m->command);
m->send_arduino_command(m->arduino_command);
}
void Meteorologger::arduino_threshold_callback(Meteorologger * m)
{
if (m->response == "")
if (m->arduino_response == "")
{
m->repl->log("\n[ERROR] " + String(m->arduino_threshold) + " secs "
"without Arduino SWSerial response.");
if (m->interval > 0)
"without Arduino SWSerial response. Stopped waiting.");
if (m->interval_minutes > 0)
{
m->status_level=STATUS_LEVEL_ERROR_SWSER;
m->status_level=STATUS_ERROR_SWSER;
}
}
m->ticker_arduino_threshold.detach();
......@@ -110,6 +146,12 @@ void Meteorologger::notify_status_callback(Meteorologger * m)
void Meteorologger::blink_status_callback(Meteorologger * m)
{
if (m->interval_minutes == 0)
{
m->ticker_blink_status.detach();
m->notify_off();
return;
}
m->pin_notify_state = !m->pin_notify_state;
digitalWrite(m->pin_notify, m->pin_notify_state);
if (m->blink_count > 0)
......@@ -139,3 +181,172 @@ void Meteorologger::notify_off(void)
digitalWrite(pin_notify, this->pin_notify_state);
this->ticker_notify_status.detach();
}
void Meteorologger::taildatalog(void)
{
sdcard_tail(this->datalog_filename);
}
void Meteorologger::set_board(int board_id, String user_password)
{
if (board_id == 0)
{
this->board_id=0;
this->board_hash="";
this->save_config();
this->print_config();
return;
}
JsonObject& root = jsonBuffer.createObject();
root["board_id"] = board_id;
root["user_password"] = user_password;
String payload;
root.printTo(payload);
String response = wpost(APIURL_GETBOARDHASH, "application/json", payload);
JsonObject& root_resp = jsonBuffer.parseObject(response);
String board_hash = root_resp["board_hash"];
if (board_hash != "")
{
this->board_id = board_id;
this->board_hash = board_hash;
this->save_config(true);
}
else
{
this->repl->log("[DEBUG] Response:\n" + response);
}
}
bool Meteorologger::save_config(bool print_after)
{
File fp = SPIFFS.open(this->config_filename.c_str(), "w");
if (!fp)
{
this->repl->log("\n[ERROR] SPIFFS unable to write file '" +
this->config_filename + "'");
return false;
}
JsonObject& root = jsonBuffer.createObject();
root["board_id"] = String(this->board_id);
root["board_hash"] = String(this->board_hash);
root["interval_minutes"] = String(this->interval_minutes);
root["arduino_command"] = String(this->arduino_command);
root.prettyPrintTo(fp);
fp.close();
this->repl->log("\n[INFO] Sucessfully updated '" + this->config_filename
+ "' (SPIFFS)");
if (print_after)
{
this->repl->log("\n");
root.prettyPrintTo(Serial);
}
return true;
}
void Meteorologger::load_config(void)
{
File fp = SPIFFS.open(this->config_filename.c_str(), "r");
if (!fp)
{
this->repl->log("\n[ERROR] SPIFFS unable to read file '" +
this->config_filename + "'");
return;
}
JsonObject& root = jsonBuffer.parseObject(fp);
this->board_id = root["board_id"].as<String>().toInt();
this->board_hash = root["board_hash"].as<String>();
this->interval_minutes = String(
root["interval_minutes"].as<String>()).toFloat();
this->arduino_command = root["arduino_command"].as<String>();
fp.close();
}
void Meteorologger::print_config(void)
{
File fp = SPIFFS.open(this->config_filename.c_str(), "r");
if (!fp)
{
this->repl->log("\n[ERROR] SPIFFS unable to read file '" +
this->config_filename + "'");
return;
}
Serial.println();
while (fp.available()) Serial.write(fp.read());
fp.close();
Serial.println();
}
String Meteorologger::generate_json(void)
{
JsonObject& root = jsonBuffer.createObject();
JsonObject& root_sensors = jsonBuffer.createObject();
JsonObject& root_datetime = jsonBuffer.createObject();
JsonObject& root_data = jsonBuffer.createObject();
String sensors=this->arduino_command;
sensors.replace("read,", "");
sensors.replace(",", "\",\"");
JsonArray& array_sensors = jsonBuffer.parseArray("[\"" + sensors + "\"]");
String values=this->arduino_response;
values.replace(",", "\",\"");
JsonArray& array_values = jsonBuffer.parseArray("[\"" + values + "\"]");
for (int i=0; i < array_values.size(); i++)
{
String sensor_name = array_sensors[i].as<String>();
if (sensor_name == "dt")
{
root_datetime["value"] = array_values[i];
root_datetime["format"] = String(this->datetime_format);
root_datetime["source"] = "RTC";
}
else if (sensor_name == "T")
{
root_sensors["DHT22_TEMP"] = array_values[i];
}
else if (sensor_name == "t")
{
root_sensors["BMP085_TEMP"] = array_values[i];
}
else if (sensor_name == "p")
{
root_sensors["BMP085_PRESSURE"] = array_values[i];
}
else if (sensor_name == "l")
{
root_sensors["LDR"] = array_values[i];
}
else
{
root_sensors[sensor_name] = array_values[i];
}
}
root_data["datetime"] = root_datetime;
root_data["sensors"] = root_sensors;
JsonArray& array_data = jsonBuffer.createArray();
array_data.add(root_data);
root["board_hash"] = String(this->board_hash);
root["data"] = array_data;
String json;
root.prettyPrintTo(json);
return json;
}
bool Meteorologger::server_upload(String json)
{
this->repl->log("\n[INFO] Attemping upload to server..");
String response=wpost(APIURL_POSTRAWSENSORDATA, "application/json", json);
this->repl->log("\n[DEBUG] Response:\n" + response);
response.trim();
JsonObject& root = jsonBuffer.parseObject(response);
if (response="" || root.get<String>("error")) return false;
return true;
}
......@@ -13,41 +13,61 @@
#include "repl.h"
#define STATUS_LEVEL_INACTIVE 0
#define STATUS_LEVEL_OK 1
#define STATUS_LEVEL_ERROR_SWSER 2
#define STATUS_LEVEL_ERROR_SD 3
#define STATUS_LEVEL_ERROR_SERVER 4
#define STATUS_INACTIVE 0
#define STATUS_OK 1
#define STATUS_ERROR_SWSER 2
#define STATUS_ERROR_SERVER 3
#define STATUS_ERROR_SD_ONLY 4
#define STATUS_ERROR_SD_SERVER 5 // CRITICAL: DATA LOSS!!
#define APIURL_GETBOARDHASH \
"http://dados.cta.if.ufrgs.br/emm/api/get/boardhash"
#define APIURL_POSTRAWSENSORDATA \
"http://dados.cta.if.ufrgs.br/emm/api/post/rawsensordata"
class Meteorologger
{
public:
Meteorologger(SoftwareSerial * arduinoSerial,
REPL * repl,
String datalog_filename);
Meteorologger(REPL * repl, SoftwareSerial * arduinoSerial,
String datalog_filename,
String config_filename);
String datalog_filename;
float interval=0;
String command="";
String response="";
float arduino_threshold=3;
String config_filename;
int board_id=0;
String board_hash="";
float interval_minutes=0;
String arduino_command="";
String datetime_format="%Y-%m-%d %H:%M:%S";
String arduino_response="";
float arduino_threshold=5;
bool arduino_waiting=false;
int pin_notify=2;
bool notify_high=false;
float notify_interval=5;
float notify_interval=2;
float blink_interval=0.15;
void begin();
void activate(float interval, String command);
void update(void);
void sendCommand(String command);
void send_arduino_command(String command);
String generate_json(void);
bool server_upload(String json);
void set_board(int board_id, String user_password);
void taildatalog(void);
void load_config(void);
bool save_config(bool print_after=false);
void print_config(void);
protected:
int status_level=STATUS_LEVEL_INACTIVE;
int status_level=STATUS_INACTIVE;
bool pin_notify_state;
int blink_count=0;
SoftwareSerial * arduinoSerial;
REPL * repl;
......
......@@ -187,7 +187,7 @@ int REPL::_execute_command(void)
rc++;
}
Serial.print("\n[ERROR] invalid command \"");
Serial.print("\n[ERROR] invalid REPL command \"");
Serial.print(command_name);
Serial.print("\"");
......
......@@ -5,6 +5,8 @@
void REPL_ARDUINO(REPL * repl);
void REPL_METEOROLOG(REPL * repl);
void REPL_TAILDATALOG(REPL * repl);
void REPL_SETBOARD(REPL * repl);
void REPL_SHOWCONFIG(REPL * repl);
#define CMD_REPL_ARDUINO { \
......@@ -16,20 +18,38 @@ void REPL_TAILDATALOG(REPL * repl);
#define CMD_REPL_METEOROLOG { \
"meteorolog", \
REPL_METEOROLOG, \
"meteorolog <interval_mins> <arduino_command>", \
"Activate meterolog timer with interval (minutes)."}
"meteorolog <interval_minutes> <arduino_command>", \
"Activate meterolog timer with given interval (minutes). " \
"Parameters are saved at config file for auto-start on boot."}
#define CMD_REPL_TAILDATALOG { \
"taildatalog", \
REPL_TAILDATALOG, \
"taildatalog", \
"Print the current datalog file."}
"Print last N lines of the the current datalog file."}
#define CMD_REPL_SETBOARD { \
"setboard", \
REPL_SETBOARD, \
"setboard <board_id> <user_password>", \
"Set board authentication token from server to config file."}
#define CMD_REPL_SHOWCONFIG { \
"showconfig", \
REPL_SHOWCONFIG, \
"showconfig", \
"Show the configuration file."}
#define ALLCMD_REPL_METEOROLOGGER \