I am modifying the serialProxy plugin to read serial data from battery inverters/UPS systems that support the Megatec serial protocol. It's working but im stuck on one thing. How do i read a value from a text box in the plugin web page and assign it to a variable?
The item im interested in is...........
Code: Select all
#define P099_BATTERY_V PCONFIG_LONG(1)
#define P099_BATTERY_V_LABEL PCONFIG_LABEL(1)
Code: Select all
#ifdef USES_P099
// #######################################################################################################
// #################### Plugin 099 Serial SNADI ##########################################################
// #######################################################################################################
//
// Interact with a device connected to serial
// Allows to redirect data to a controller
//
#include <ESPeasySerial.h>
#define PLUGIN_099
#define PLUGIN_ID_099 99
#define PLUGIN_NAME_099 "Communication - Serial SNADI [TESTING]"
#define P099_BAUDRATE PCONFIG_LONG(0)
#define P099_BAUDRATE_LABEL PCONFIG_LABEL(0)
#define P099_BATTERY_V PCONFIG_LONG(1) //RAE
#define P099_BATTERY_V_LABEL PCONFIG_LABEL(1)
#define P099_QUERY_VALUE 0 // Temp placement holder until we know what selectors are needed.
#define P099_NR_OUTPUT_OPTIONS 1
#define P099_NR_OUTPUT_VALUES 1
#define P099_QUERY1_CONFIG_POS 3
#define P099_DEFAULT_BAUDRATE 2400
#define P099_DEFAULT_BATTERY_V 48
#define P99_Nlines 1 //Was 2 RAE
#define P99_Nchars 64
#define P099_INITSTRING 0
#define P099_EXITSTRING 1
long int batteryV = 0;
//batteryV = getFormItemInt(P099_BATTERY_V);
//batteryV = getFormItemInt(P099_DEFAULT_BATTERY_V);
struct P099_data_struct : public PluginTaskData_base {
P099_data_struct() : P099_easySerial(nullptr) {}
~P099_data_struct() {
reset();
}
void reset() {
if (P099_easySerial != nullptr) {
delete P099_easySerial;
P099_easySerial = nullptr;
}
}
bool init(const int16_t serial_rx, const int16_t serial_tx, unsigned long baudrate) {
if ((serial_rx < 0) && (serial_tx < 0)) {
return false;
}
reset();
P099_easySerial = new ESPeasySerial(serial_rx, serial_tx);
if (isInitialized()) {
P099_easySerial->begin(baudrate);
return true;
}
return false;
}
bool isInitialized() const {
return P099_easySerial != nullptr;
}
void sendString(String& data) {
//void sendString(const String& data) {
if (isInitialized()) {
if (data.length() > 0) {
data += '\r'; //added cr rae
P099_easySerial->write(data.c_str());
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
String log = F("Proxy: Sending: ");
log += data;
addLog(LOG_LEVEL_INFO, log);
}
}
}
}
bool loop() {
if (!isInitialized()) {
return false;
}
bool fullSentenceReceived = false;
if (P099_easySerial != nullptr) {
while (P099_easySerial->available() > 0 && !fullSentenceReceived) {
// Look for end marker
char c = P099_easySerial->read();
switch (c) {
case 13:
{
const size_t length = sentence_part.length();
bool valid = length > 0;
for (size_t i = 0; i < length && valid; ++i) {
if ((sentence_part[i] > 127) || (sentence_part[i] < 32)) {
sentence_part = "";
++sentences_received_error;
valid = false;
}
}
if (valid) {
//We have a valid serial string
fullSentenceReceived = true;
//*******************************************************************************
parsedata(sentence_part); //Function call to parse the data ..RAE
calcBatteryVolts(); //Function call to calculate the battery voltage based on cell V ..RAE
//*******************************************************************************
}
break;
}
case 10:
// Ignore LF
break;
default:
sentence_part += c;
break;
}
if (max_length_reached()) { fullSentenceReceived = true; }
}
}
if (fullSentenceReceived) {
++sentences_received;
length_last_received = sentence_part.length();
}
return fullSentenceReceived;
}
void getSentence(String& string) {
string = sentence_part;
sentence_part = "";
}
void getSentencesReceived(uint32_t& succes, uint32_t& error, uint32_t& length_last) const {
succes = sentences_received;
error = sentences_received_error;
length_last = length_last_received;
}
void setMaxLength(uint16_t maxlenght) {
max_length = maxlenght;
}
private:
bool max_length_reached() const {
if (max_length == 0) { return false; }
return sentence_part.length() >= max_length;
}
ESPeasySerial *P099_easySerial = nullptr;
String sentence_part;
uint16_t max_length = 550;
uint32_t sentences_received = 0;
uint32_t sentences_received_error = 0;
uint32_t length_last_received = 0;
};
// Plugin settings:
// Validate:
// - [0..9]
// - "+", "-", "."
// - [A..Z]
// - [a..z]
// - ASCII 32 - 217
// Sentence start: char
// Sentence end: CR/CRLF/LF/char
// Max length sentence: 1k max
// Interpret as:
// - Float
// - int
// - String
// Init string (incl parsing CRLF like characters)
// Timeout between sentences.
boolean Plugin_099(byte function, struct EventStruct *event, String& string) {
boolean success = false;
switch (function) {
case PLUGIN_DEVICE_ADD: {
Device[++deviceCount].Number = PLUGIN_ID_099;
Device[deviceCount].Type = DEVICE_TYPE_DUAL;
Device[deviceCount].VType = SENSOR_TYPE_STRING;
Device[deviceCount].Ports = 0;
Device[deviceCount].PullUpOption = false;
Device[deviceCount].InverseLogicOption = false;
Device[deviceCount].FormulaOption = false;
Device[deviceCount].ValueCount = 1; //rae
Device[deviceCount].SendDataOption = false; //Send to controller rae
Device[deviceCount].TimerOption = true;
Device[deviceCount].GlobalSyncOption = false;
break;
}
//Return the device name
case PLUGIN_GET_DEVICENAME: {
string = F(PLUGIN_NAME_099);
break;
}
//called when the user opens the module configuration page
//it allows to add a new row for each output variable of the plugin
case PLUGIN_GET_DEVICEVALUENAMES: {
for (byte i = 0; i < VARS_PER_TASK; ++i) {
if (i < P099_NR_OUTPUT_VALUES) {
const byte pconfigIndex = i + P099_QUERY1_CONFIG_POS;
byte choice = PCONFIG(pconfigIndex);
safe_strncpy(
ExtraTaskSettings.TaskDeviceValueNames[i],
Plugin_099_valuename(choice, false),
sizeof(ExtraTaskSettings.TaskDeviceValueNames[i]));
} else {
ZERO_FILL(ExtraTaskSettings.TaskDeviceValueNames[i]);
}
}
break;
}
case PLUGIN_GET_DEVICEGPIONAMES: {
serialHelper_getGpioNames(event, false, true); // TX optional
break;
}
case PLUGIN_WEBFORM_SHOW_VALUES:
{
P099_data_struct *P099_data =
static_cast<P099_data_struct *>(getPluginTaskData(event->TaskIndex));
if ((nullptr != P099_data) && P099_data->isInitialized()) {
uint32_t success, error, length_last;
P099_data->getSentencesReceived(success, error, length_last);
byte varNr = VARS_PER_TASK;
addHtml(pluginWebformShowValue(event->TaskIndex, varNr++, F("Success"), String(success)));
addHtml(pluginWebformShowValue(event->TaskIndex, varNr++, F("Error"), String(error)));
addHtml(pluginWebformShowValue(event->TaskIndex, varNr++, F("Length Last"), String(length_last), true));
// success = true;
}
break;
}
case PLUGIN_SET_DEFAULTS:
{
P099_BAUDRATE = P099_DEFAULT_BAUDRATE;
//RAE
//P099_BATTERY_V = P099_DEFAULT_BATTERY_V;
//batteryV = P099_BATTERY_V;
success = true;
break;
}
case PLUGIN_WEBFORM_SHOW_CONFIG:
{
string += serialHelper_getSerialTypeLabel(event);
success = true;
break;
}
case PLUGIN_WEBFORM_LOAD: {
serialHelper_webformLoad(event);
/*
P099_data_struct *P099_data =
static_cast<P099_data_struct *>(getPluginTaskData(event->TaskIndex));
if (nullptr != P099_data && P099_data->isInitialized()) {
String detectedString = F("Detected: ");
detectedString += String(P099_data->P099_easySerial->baudRate());
addUnit(detectedString);
*/
addFormNumericBox(F("Baudrate"), P099_BAUDRATE_LABEL, P099_BAUDRATE, 2400, 115200);
addUnit(F("baud"));
//RAE
addFormNumericBox(F("Battery V"), P099_BATTERY_V_LABEL, P099_BATTERY_V, 12, 96);
addUnit(F("12, 24, 48, 96 Volts"));
/*
{
// In a separate scope to free memory of String array as soon as possible
sensorTypeHelper_webformLoad_header();
String options[P099_NR_OUTPUT_OPTIONS];
for (int i = 0; i < P099_NR_OUTPUT_OPTIONS; ++i) {
options[i] = Plugin_099_valuename(i, true);
}
for (byte i = 0; i < P099_NR_OUTPUT_VALUES; ++i) {
const byte pconfigIndex = i + P099_QUERY1_CONFIG_POS;
sensorTypeHelper_loadOutputSelector(event, pconfigIndex, i, P099_NR_OUTPUT_OPTIONS, options);
}
}
*/
{
String strings[P99_Nlines];
LoadCustomTaskSettings(event->TaskIndex, strings, P99_Nlines, P99_Nchars);
for (byte varNr = 0; varNr < P99_Nlines; varNr++)
{
String label = F("Send control char ");
label += String(varNr + 1);
addFormTextBox(label, getPluginCustomArgName(varNr), strings[varNr], P99_Nchars);
}
}
P099_html_show_stats(event);
success = true;
break;
}
//This defines the code to be executed when the form is submitted
//the plugin settings should be saved to Settings.TaskDevicePluginConfig[event->TaskIndex][x]
case PLUGIN_WEBFORM_SAVE: {
serialHelper_webformSave(event);
P099_BAUDRATE = getFormItemInt(P099_BAUDRATE_LABEL);
//RAE
P099_BATTERY_V = getFormItemInt(P099_BATTERY_V_LABEL);
String error;
char P099_deviceTemplate[P99_Nlines][P99_Nchars];
for (byte varNr = 0; varNr < P99_Nlines; varNr++)
{
if (!safe_strncpy(P099_deviceTemplate[varNr], WebServer.arg(getPluginCustomArgName(varNr)), P99_Nchars)) {
error += getCustomTaskSettingsError(varNr);
}
}
if (error.length() > 0) {
addHtmlError(error);
}
SaveCustomTaskSettings(event->TaskIndex, (byte *)&P099_deviceTemplate, sizeof(P099_deviceTemplate));
success = true;
break;
}
case PLUGIN_INIT: {
const int16_t serial_rx = CONFIG_PIN1;
const int16_t serial_tx = CONFIG_PIN2;
initPluginTaskData(event->TaskIndex, new P099_data_struct());
P099_data_struct *P099_data =
static_cast<P099_data_struct *>(getPluginTaskData(event->TaskIndex));
if (nullptr == P099_data) {
return success;
}
//batteryV = getFormItemInt(P099_BATTERY_V_LABEL);
if (P099_data->init(serial_rx, serial_tx, P099_BAUDRATE)) {
success = true;
if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
String log = F("Serial : Init OK ESP GPIO-pin RX:");
log += serial_rx;
log += F(" TX:");
log += serial_tx;
addLog(LOG_LEVEL_DEBUG, log);
}
} else {
clearPluginTaskData(event->TaskIndex);
}
break;
}
case PLUGIN_EXIT: {
clearPluginTaskData(event->TaskIndex);
success = true;
break;
}
case PLUGIN_FIFTY_PER_SECOND: {
if (Settings.TaskDeviceEnabled[event->TaskIndex]) {
P099_data_struct *P099_data =
static_cast<P099_data_struct *>(getPluginTaskData(event->TaskIndex));
if ((nullptr != P099_data) && P099_data->loop()) {
// schedule_task_device_timer(event->TaskIndex, millis() + 10);
delay(0); // Processing a full sentence may take a while, run some
// background tasks.
P099_data->getSentence(event->String2);
sendData(event);
}
success = true;
}
break;
}
case PLUGIN_READ: {
P099_data_struct *P099_data =
static_cast<P099_data_struct *>(getPluginTaskData(event->TaskIndex));
if ((nullptr != P099_data)) {
String strings[P99_Nlines];
LoadCustomTaskSettings(event->TaskIndex, strings, P99_Nlines, P99_Nchars);
parseSystemVariables(strings[0], false);
P099_data->sendString(strings[0]);
}
break;
}
}
return success;
}
String Plugin_099_valuename(byte value_nr, bool displayString) {
switch (value_nr) {
case P099_QUERY_VALUE: return displayString ? F("Value") : F("v");
}
return "";
}
void P099_html_show_stats(struct EventStruct *event) {
P099_data_struct *P099_data =
static_cast<P099_data_struct *>(getPluginTaskData(event->TaskIndex));
if ((nullptr == P099_data) || !P099_data->isInitialized()) {
return;
}
{
addRowLabel(F("Current Sentence"));
String sentencePart;
P099_data->getSentence(sentencePart);
addHtml(sentencePart);
}
{
addRowLabel(F("Sentences (pass/fail)"));
String chksumStats;
uint32_t success, error, length_last;
P099_data->getSentencesReceived(success, error, length_last);
chksumStats = success;
chksumStats += '/';
chksumStats += error;
addHtml(chksumStats);
addRowLabel(F("Length Last Sentence"));
addHtml(String(length_last));
}
}
//***RAE SNADI Functions****************************************************************************************************************************************************************
//********************************************************************************************************************************************************************************
void parsedata(String sentence_part)
{
//Print to debug Lenghth: string
const size_t length = sentence_part.length();
String log = F("SentencePart: "); //rae
log += length;
log += ": ";
log += sentence_part; //rae
addLog(LOG_LEVEL_DEBUG, log); //rae
//Parse the data string
sentence_part.remove(0, 1); //Remove '(' leading character
float parsed_data[10]; //Float array for the parsed data
int index = 0; //Reset index counter
String string_convert; //String
char sentence_array[50]; //String array for raw data from the serial port
sentence_part.toCharArray(sentence_array, length); //Convert string raw data to char array
char *p = sentence_array; //create sentence_array address pointer p
char *str; //create str pointer
index = 0; //set counter to 0
while ((str = strtok_r(p, " ", &p)) != NULL) { //parse data use space as seperator
string_convert = str; //???
parsed_data[index] = string_convert.toFloat(); //Convert string to float
UserVar[0 + index] = parsed_data[index]; //Write 8 values to dummy vars in task 1+2
index++; //counter + 1 - loop count
}
//Print to debug Lenghth: parsed_data
log = F("Parsed Data: "); //Title
log += length; //Data length
log += ": "; //Seperator
for (int i = 0; i <= 7; i++) { //Print all vars
log += parsed_data[i];
log += ": ";
}
addLog(LOG_LEVEL_DEBUG, log);
return;
}
void calcBatteryVolts()
{
//Snadi reports the cell voltage so we need to multiply this
//given 2v per cell to get the real battery voltage.
float batteryCalcV = 0.0;
float batteryCellV = UserVar[0 + 5];
switch (batteryV) {
case 12:
batteryCalcV = batteryCellV * 6;
break;
case 24:
batteryCalcV = batteryCellV * 12;
break;
case 48:
batteryCalcV = batteryCellV * 24;
break;
case 96:
batteryCalcV = batteryCellV * 48;
break;
}
UserVar[0 + 5] = batteryCalcV;
//Print to debug Lenghth: parsed_data
String log;
log = F("batteryCalcV: ");
log += batteryCalcV;
addLog(LOG_LEVEL_DEBUG, log);
return;
}
#endif // USES_P099