Commit 637df50d authored by Nelso Jost's avatar Nelso Jost
Browse files

FIX refactor meteorolog base code to eliminate delay()

parent a7ed9d7e
......@@ -2,8 +2,8 @@ include ../utils/platformio/Makefile
INOPROJECT = meteorolog
BAUD = 115200
BOARD = uno
BAUD = 9600
BOARD = nanoatmega328
monitor: qtmonitor
#
......@@ -8,7 +8,7 @@
#include "mysensors.h"
#include "boardcommands.h"
#define BAUDRATE 115200
#define BAUDRATE 9600
String stream_line;
......@@ -25,6 +25,7 @@ void loop()
if (Serial.available())
{
stream_line = Serial.readString();
Serial.println(execute_board_command(stream_line.trim()));
//stream_line.trim();
Serial.println(execute_board_command(stream_line));
}
}
......@@ -4,46 +4,27 @@
* Purpose: Entry point for the Arduino Toolchain.
*---------------------------------------------------------------------------*/
#include <Arduino.h>
#include <Ticker.h>
#include <SPI.h>
#include <SD.h>
#include <SoftwareSerial.h>
#include "nodemcu_pinout.h"
#include "repl.h"
#include "repl_gpio.h"
#include "repl_sdcard.h"
#include "repl_wifi.h"
#include "repl_meteorologger.h"
#define BAUDRATE 115200
#define DATALOG_FILENAME "datalog.txt"
#define PIN_LED D3
#define PIN_BUTTON D0
#define ARDUINO_TX D1 // (soft RX)
#define ARDUINO_RX D2 // (soft TX)
#define ARDUINO_BAUDRATE 115200
const int chipSelect = 4;
void REPL_ARDUINO(REPL * repl);
void REPL_METEOROLOG(REPL * repl);
#include "nodemcu_pinout.h"
#include "meteorologger.h"
#define BAUDRATE 115200
#define DATALOG_FILENAME "datalog.txt"
typedef struct
{
REPL * repl;
float interval;
String command;
String board_hash;
} METEOROLOG_args;
#define ARDUINO_TX D1 // (soft RX)
#define ARDUINO_RX D2 // (soft TX)
#define ARDUINO_BAUDRATE 9600
#define CS_PIN D8
void meteorolog_callback(METEOROLOG_args * args);
void sd_datalog(String datalog, REPL * repl);
void server_datalog(String datalog, REPL * repl);
#define PIN_NOTIFY_ACTIVE D4
REPL_COMMAND commands[] =\
......@@ -51,158 +32,45 @@ REPL_COMMAND commands[] =\
ALLCMD_REPL_GPIO,
ALLCMD_REPL_SDCARD,
ALLCMD_REPL_WIFI,
{"arduino", REPL_ARDUINO, "arduino <command>",
"Send command to Arduino via software serial."},
{"meteorolog", REPL_METEOROLOG, "meteorolog <interval_secs> <arduino_command>",
"Activate meterolog timer on given interval (seconds)."},
ALLCMD_REPL_METEOROLOGGER,
{NULL}
};
REPL repl(commands);
int led_state = 0;
int button_state = 0;
SoftwareSerial arduinoSerial(ARDUINO_TX, ARDUINO_RX); // RX, TX
Ticker timer_button;
Ticker timer_meteorolog;
Meteorologger meteorologger(&arduinoSerial, &repl, String(DATALOG_FILENAME));
SoftwareSerial arduinoSerial(ARDUINO_TX, ARDUINO_RX); // RX, TX
void setup()
{
Serial.begin(BAUDRATE);
arduinoSerial.begin(ARDUINO_BAUDRATE);
if (!SD.begin(chipSelect))
{
repl.log("\n[ERROR] Unable to initialize SD card.");
}
// pinMode(PIN_BUTTON, INPUT);
// timer_button.attach(0.1, toggle_led);
sdcard_begin(CS_PIN);
}
void loop()
{
repl.update();
}
void toggle_led()
{
int reading = digitalRead(PIN_BUTTON);
if (button_state != reading)
{
button_state = reading;
if (button_state == HIGH)
{
led_state = !led_state;
pinMode(PIN_LED, OUTPUT);
digitalWrite(PIN_LED, led_state);
}
}
meteorologger.update();
}
void REPL_ARDUINO(REPL * repl)
{
Serial.println();
arduinoSerial.print(repl->get_arg(1));
delay(1500);
while (arduinoSerial.available())
{
Serial.write(arduinoSerial.read());
}
meteorologger.sendCommand(repl->get_arg(1));
}
void REPL_METEOROLOG(REPL * repl)
{
METEOROLOG_args * args = new METEOROLOG_args;
args->interval = repl->get_arg(1).toFloat();
args->command = repl->get_arg(2);
args->board_hash = repl->get_arg(3);
args->repl = repl;
if (args->interval == 0)
{
timer_meteorolog.detach();
repl->log("\n[INFO] Deactivated meteorolog timer.");
}
else
{
timer_meteorolog.attach(args->interval, meteorolog_callback, args);
repl->log("\n[INFO] Activated meteorolog timer with " +
String(args->interval) + " s interval.");
meteorolog_callback(args);
}
meteorologger.activate(repl->get_arg(1).toFloat(), repl->get_arg(2));
}
void meteorolog_callback(METEOROLOG_args * args)
void REPL_TAILDATALOG(REPL * repl)
{
args->repl->log("\n[INFO] Performing datalog..");
Serial.println();
arduinoSerial.print(args->command);
args->repl->log("\n[INFO] Sent command '" + args->command + "' to arduino.");
delay(1500);
String response="";
while (arduinoSerial.available()) { response += arduinoSerial.read(); }
response.trim();
args->repl->log("\n[INFO] Got response: '" + response + "'");
if (response.length() > 0)
{
sd_datalog(response, args->repl);
// server_datalog(response, args->repl);
}
else
{
args->repl->log("\n[ERROR] Empty datalog response");
}
sdcard_tail(meteorologger.datalog_filename);
}
void sd_datalog(String datalog, REPL * repl)
{
Sd2Card sd_card;
SdVolume sd_volume;
if (!sd_card.init(SPI_HALF_SPEED, chipSelect))
{
repl->log("\n[ERROR] Unable to detect/init SD card.");
return;
}
if (!sd_volume.init(sd_card))
{
repl->log("\n[ERROR] SD card is plugged but no volume was found.");
return;
}
File sd_file = SD.open(DATALOG_FILENAME, FILE_WRITE);
if (!sd_file)
{
repl->log("\n[ERROR] Unable to open '" + String(DATALOG_FILENAME) + "'");
return;
}
int written = sd_file.println(datalog);
sd_file.close();
repl->log("\n[INFO] " + String(written) + " bytes written at '" +
String(DATALOG_FILENAME) + "'");
}
void server_datalog(String datalog, REPL * repl)
{
String response = String("POST emm/api/post/rawsensordata HTTP/1.1\r\n") + \
"Host: dados.cta.if.ufrgs.br" + \
"Content-type: Application/JSON";
// TODO: IMPLEMENT HTML CLIENT REST API
}
#include <Arduino.h>
#include <SoftwareSerial.h>
#include "meteorologger.h"
#include "repl.h"
#include "sdcard.h"
Meteorologger::Meteorologger(SoftwareSerial * arduinoSerial,
REPL * repl,
String datalog_filename)
{
this->arduinoSerial = arduinoSerial;
this->repl = repl;
this->datalog_filename = datalog_filename;
pinMode(this->pin_notify, OUTPUT);
}
void Meteorologger::update(void)
{
if (!this->arduinoSerial->available()) return;
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)");
if (this->interval == 0) return;
if (!sdcard_write(this->datalog_filename, this->response + "\n"))
{
this->status_level = STATUS_LEVEL_ERROR_SD;
return;
}
this->status_level = STATUS_LEVEL_OK;
}
void Meteorologger::activate(float interval, String command)
{
this->interval = 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);
}
else
{
this->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);
}
}
void Meteorologger::sendCommand(String command)
{
this->response="";
this->command = command;
this->repl->log("\n[DEBUG] Sending command '" + this->command +
"' to arduino.");
this->arduinoSerial->print(this->command);
this->ticker_arduino_threshold.attach(this->arduino_threshold,
this->arduino_threshold_callback, this);
}
void Meteorologger::meteorolog_callback(Meteorologger * m)
{
m->sendCommand(m->command);
}
void Meteorologger::arduino_threshold_callback(Meteorologger * m)
{
if (m->response == "")
{
m->repl->log("\n[ERROR] " + String(m->arduino_threshold) + " secs "
"without Arduino SWSerial response.");
if (m->interval > 0)
{
m->status_level=STATUS_LEVEL_ERROR_SWSER;
}
}
m->ticker_arduino_threshold.detach();
}
void Meteorologger::notify_status_callback(Meteorologger * m)
{
m->blink_count = m->status_level*2 - 1;
m->ticker_blink_status.attach(m->blink_interval,
m->blink_status_callback, m);
m->notify_off();
}
void Meteorologger::blink_status_callback(Meteorologger * m)
{
m->pin_notify_state = !m->pin_notify_state;
digitalWrite(m->pin_notify, m->pin_notify_state);
if (m->blink_count > 0)
{
m->blink_count -= 1;
}
else
{
m->ticker_blink_status.detach();
m->notify_on();
}
}
void Meteorologger::notify_on(void)
{
this->pin_notify_state = this->notify_high;
digitalWrite(pin_notify, this->pin_notify_state);
this->ticker_notify_status.attach(this->notify_interval,
this->notify_status_callback, this);
}
void Meteorologger::notify_off(void)
{
this->pin_notify_state = !this->notify_high;
digitalWrite(pin_notify, this->pin_notify_state);
this->ticker_notify_status.detach();
}
/*-----------------------------------------------------------------------------
* Author: Nelso G. Jost (nelsojost@gmail.com)
* License: GPLv2
* Purpose:Implements the meteorological datalog functionality.
*---------------------------------------------------------------------------*/
#ifndef METEOROLOGGER_H
#define METEOROLOGGER_H
#include <Ticker.h>
#include <SoftwareSerial.h>
#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
class Meteorologger
{
public:
Meteorologger(SoftwareSerial * arduinoSerial,
REPL * repl,
String datalog_filename);
String datalog_filename;
float interval=0;
String command="";
String response="";
float arduino_threshold=3;
int pin_notify=2;
bool notify_high=false;
float notify_interval=5;
float blink_interval=0.15;
void activate(float interval, String command);
void update(void);
void sendCommand(String command);
protected:
int status_level=STATUS_LEVEL_INACTIVE;
bool pin_notify_state;
int blink_count=0;
SoftwareSerial * arduinoSerial;
REPL * repl;
Ticker ticker_meteorolog;
Ticker ticker_arduino_threshold;
Ticker ticker_notify_status;
Ticker ticker_blink_status;
void notify_on(void);
void notify_off(void);
static void meteorolog_callback(Meteorologger * m);
static void arduino_threshold_callback(Meteorologger * m);
static void notify_status_callback(Meteorologger * m);
static void blink_status_callback(Meteorologger * m);
};
#endif
#ifndef REPL_METEOROLOGGER_H
#define REPL_METEOROLOGGER_H
void REPL_ARDUINO(REPL * repl);
void REPL_METEOROLOG(REPL * repl);
void REPL_TAILDATALOG(REPL * repl);
#define CMD_REPL_ARDUINO { \
"arduino", \
REPL_ARDUINO, \
"arduino <command>", \
"Send command to Arduino via software serial."}
#define CMD_REPL_METEOROLOG { \
"meteorolog", \
REPL_METEOROLOG, \
"meteorolog <interval_mins> <arduino_command>", \
"Activate meterolog timer with interval (minutes)."}
#define CMD_REPL_TAILDATALOG { \
"taildatalog", \
REPL_TAILDATALOG, \
"taildatalog", \
"Print the current datalog file."}
#define ALLCMD_REPL_METEOROLOGGER \
CMD_REPL_ARDUINO, \
CMD_REPL_METEOROLOG, \
CMD_REPL_TAILDATALOG
#endif
......@@ -4,13 +4,31 @@
* Purpose: Provide basic SD card management commands for the REPL.
*---------------------------------------------------------------------------*/
#include <Arduino.h>
#include "repl.h"
#include "sdcard.h"
#include "repl_sdcard.h"
void REPL_SDINFO(REPL * repl)
{
print_sdcard_info();
sdcard_info();
}
void REPL_SDCAT(REPL * repl)
{
sdcard_cat(repl->get_arg(1));
}
void REPL_SDTAIL(REPL * repl)
{
sdcard_tail(repl->get_arg(1));
}
void REPL_SDWRITE(REPL * repl)
{
sdcard_write(repl->get_arg(1), repl->get_arg(2));
}
......@@ -9,14 +9,34 @@
#define CMD_REPL_SDINFO {\
"sdinfo", REPL_SDINFO, "sdinfo", \
"Show a summary of the SD card info."}
"Show a summary of the SD card info including all files."}
#define CMD_REPL_SDCAT {\
"sdcat", REPL_SDCAT, "sdcat <filename>", \
"Show contents of a SD card file."}
#define CMD_REPL_SDTAIL {\
"sdtail", REPL_SDTAIL, "sdtail <filename>", \
"Show last N lines of a SD card file."}
#define CMD_REPL_SDWRITE {\
"sdwrite", REPL_SDWRITE, "sdwrite <filename> <line>", \
"Write (append) line string to a SD card file."}
#define ALLCMD_REPL_SDCARD\
CMD_REPL_SDINFO\
CMD_REPL_SDINFO, \
CMD_REPL_SDCAT, \
CMD_REPL_SDTAIL, \
CMD_REPL_SDWRITE
#include "sdcard.h"
void REPL_SDINFO(REPL * repl);
void REPL_SDCAT(REPL * repl);
void REPL_SDTAIL(REPL * repl);
void REPL_SDWRITE(REPL * repl);
#endif
......@@ -5,66 +5,78 @@
*---------------------------------------------------------------------------*/
#include <SPI.h>
#include <SD.h>
#include "sdcard.h"
Sd2Card card;
SdVolume volume;
SdFile root;
const int chipSelect = 4;
#include "sdcard.h"
int __chip_select_pin=5;
Sd2Card sd_card;
SdVolume sd_volume;
SdFile sd_root;
/* Credits:
* https://github.com/esp8266/Arduino/tree/master/libraries/SD/examples/CardInfo
*/
void print_sdcard_info(void)
void sdcard_begin(int chip_select)
{
Serial.print("\nInitializing SD card...");
if (!SD.begin(chip_select))
{
Serial.print("\n[ERROR] Unable to initialize SD card.");
}
__chip_select_pin=chip_select;
}
if (!card.init(SPI_HALF_SPEED, chipSelect))
{
Serial.println("initialization failed. Things to check:");
Serial.println("* is a card inserted?");
Serial.println("* is your wiring correct?");
Serial.println("* did you change the chipSelect pin to match your shield or module?");
return;
}
else
{
Serial.println("Wiring is correct and a card is present.");
}
Serial.print("\nCard type: ");
switch (card.type())
String sdcard_gettype(void)
{
switch (sd_card.type())
{