/* multiModeTest.ino John Saunders 11/27/2022 This is the SparkFun Redboard and the FlipTopBox Receiver
It provides different output formats controlled by the slide switch in the receiver.
Its purpose is to receive the continuous messages sent on the 433 ISM band from the homemade sensors,
Verify them, format them into lines with headings, and log them via oTerminal.
The receiver uses super-regenerative technique and on-off modulation in Rs-242 format at 2400 baud.
These signals are processed by a Picaxe 14M2 MCU and immediately sent to the Arduino.
The identity of the message is in its first character. The message is delineated by '<' and '>' characters.
Transmissiom is controlled by the receiver sending a RTS signal to the Aeduino after transmitting the message to it.
There are 6 messages with 2 - 4 individual fields separated by commas. These fields vary in length.
The Arduino separates each message field into its own buffer which is part of a struct.
Additional items in the struct point to 3 formatting headings. One denotes the source of the message,
the second identifies the nature of the fields, anf the optional third the units of a numerial value.
The Arduino combines the field data, and headings from a master list pointed to the formatting items from struch,
and punctuation into a line to be sent to the USB.
The lines are sent on receipt .
A blue LED in the receiver flashes each time a valid message is received.
*/
#include <SoftwareSerial.h>
#define fldLenMax 7 // Sufficient fot any of the supported messages
#define msgNum 6 // The number of supported messages
// Arduino allocations
#define picRx 4 // Picaxe interface
#define RTS 3 // Picaxe interface
SoftwareSerial picaxeLink (picRx, 5);
// Global Variables
long breakpoint; // milliseconds of last transmission
uint8_t dispMode; // Values are 49,50,52,56 up <> down
char keyCode; // used as flay indicating message arrival in mode 50
// The 6 messages are transmitted 24/7 from 4 devices which are in different locations.
// keyCodes r and s are from the Solar Sensor outside, n and z from the Gas Sensor near the kitchen,
// t is from the Dial Clock inside, and u is from the Garage Fan Control in the garage.
// Formatting for storeing the incoming data from the Picaxe and formatting the USB transmission
struct msg_t { // At the message level, for storing in individual field buffers
char msgName[7]; // For the updates line
uint8_t fieldInx; //Index into flds for the first field of the message
uint8_t fldNum; //Index of the last field of the message
uint16_t updates; //Since boot
};
msg_t msgs[msgNum] = {
// t(0) s (1) r (2) n (3) z (4) u(5)
{"Date", 0, 3, 0}, {"Env", 4, 3, 0}, {"Solar", 8, 3, 0}, {"Air", 12, 2, 0}, {"Den", 15, 1, 0}, {"Garage", 17, 3, 0},
};
// Index is keyCode - 'n' n o p q r s t u v w x y z
const int getMsgInx[13] = {3, -1, -1, -1, 2, 1, 0, 5, -1, -1, -1, -1, 4};
// Arrays for formatting the output
const char*areas[] = {
"Outside", "Solar Sensor", "Air Quality", "Inside", "Garage", // Areas 0 - 4
"Updates:", "Mode = " // Updates 5, mode 6
};
// Titles are used for typeInx, unitsInx in the field struct, and to identify the messsage
const char*titles[] = {
"Temperature = ", "Humidity = ", "Current = ", "Battery = ", // s 0 - 3
"Barometer = 29.", "Barometer = 30.", "Charging = ", "Previous = ", "Daily = ", // r 4 - 8
"Alarm = ", "High = ", "Low = ", // n 9 - 11
"Temperature = ", "Light = ", // z 12 - 13
"door is ", "fan is ", "is " // u 14 - 16
};
const char*units[] = {
" %", " MA", " V", "`F", " inHg", " MAH", " Kohm", // Units 0 - 6
"p", "own", "unning", "topped", "ool", "ot" // Garage 7 - 12
};
//This collects the pieces of a formatted output field
struct fld_t {
char fldData[fldLenMax]; //From the input message,
uint8_t areaInx; //To be written before the type, index into areas
uint8_t typeInx; //The kind of measurement,Index into title
uint8_t unitsInx; //The units of the measurement, index into units
};
fld_t flds[] = {
//t Month Date Hour Minute / 0 - 3 A - D
{"11", 255, 255, 255}, {"06", 255, 255, 255}, {"19", 255, 255, 255}, {"07", 255, 255, 255},
//s Temperature Humidity Solar Current Battery Voltage // 4 - 7 E - H
{"063", 0, 0, 3}, {"064", 0, 1, 0}, {"001", 1 , 2, 1}, {"538", 1, 3, 2},
//r Barometer Charging Voltage Today's charge Yesterday's charge // 8 - 11 I - L
{"006", 0, 4, 4}, {"049", 1, 6, 2}, {"0369", 1, 8, 5}, {"0402", 1, 7, 5},
//n Air Quality Alarm High Reading Low Reading // 12 - 14 M - O
{"A", 2, 9, 255}, {"09.847", 2, 10, 6}, {"09.066", 2, 11, 6},
//z Inside Temperature Inside Light // 15 - 16 P - Q
{"0087.8", 3, 12, 3}, {"00265", 4, 13, 255},
//u Garage Temperature Door Status Garage temp Status Garage Fan Status // 17 - 20 R - U
{"078", 4, 12, 3}, {"D", 4, 14, 7}, {"H", 4, 16, 11}, {"S", 4, 15, 9},
};
// This formats the output page. Letters re used for clarity. Does not include the updates line
typedef char line_t[5];
#define numLines 6
line_t outputList[numLines] = { "PER", "IFQ", "GJH", "KLD", "OMN", "STU" };
line_t messageList[] = { "ABCD", "EFGH", "IJKL", "MNO", "PQ", "RSTU"}; //
// -------------------------------------- Transmission format functions -------------------------------------
void sendUpdate(char code) { // Part of a single line at the bottom of the page
int inx;
inx = code - 'n';
int msgIndex = getMsgInx[inx];
Serial.print(msgs[msgIndex].msgName);
Serial.print(" = ");
Serial.print(msgs[msgIndex].updates, DEC);
if ((code == 'u') && (dispMode > 49)) {
Serial.println();
}
else {
Serial.print(", ");
}
}
void sendDateTime(void) { // Another part of the update line
sendField('A');
Serial.print('/');
sendField('B');
Serial.print(' ');
sendField('C');
Serial.print(':');
sendField('D');
Serial.print(", ");
}
// Functions to implement the flds array components
void sendField(char code) { // Index is into flds less 65 ('A')
int inx;
char c;
int index = (int)code - 65;
inx = flds[index].areaInx;
if ((inx < 16) && (dispMode > 49)) {
Serial.print(areas[inx]);
Serial.print(" ");
}
inx = flds[index].typeInx;
if (inx < 32) {
if ((inx == 4) && (flds[index].fldData[0] < '5')) {
inx++;
}
Serial.print(titles[inx]);
}
Serial.print(flds[index].fldData);
inx = flds[index].unitsInx;
c = flds[index].fldData[0];
if ((index == 18) && (c == 'D')) {
inx++;
}
if ((index == 19) && (c == 'H')) {
inx++;
}
if ((index == 20) && (c == 'S')) {
inx++;
}
if (inx < 20) {
Serial.print(units[inx]);
}
}
// Combines the fields
void sendLine(int index) { // Index is into outputList. Forms one line of the page
char *thisLine = outputList[index];
sendField(thisLine[0]);
Serial.print(", ");
sendField(thisLine[1]);
Serial.print(", ");
sendField(thisLine[2]);
Serial.println();
}
void sendMessage(char code) { // mode 49
int msgInx = getMsgInx[(code - 'n')]; // Index into messageList
int numFlds = msgs[msgInx].fldNum; // The number of fields in the entry
Serial.print(code);
Serial.print(", ");
sendDateTime();
Serial.print(", ");
Serial.print(areas[5]); //"Updates"
sendUpdate(code);
char *thisLine = messageList[msgInx];
for (int i = 0; i <= numFlds; i++) {
sendField(thisLine[i]);
if (i < numFlds) {
Serial.print(", ");
}
else {
Serial.println();
}
}
}
// Not mode 49
void sendPage(void) {
for (int i = 0; i < numLines; i++) {
sendLine(i);
}
Serial.print(areas[6]); //"Mode"
Serial.println(dispMode);
Serial.print(areas[5]); //"Updates"
sendDateTime();
sendUpdate('s');
Serial.print(", ");
sendUpdate('r');
Serial.print(", ");
sendUpdate('n');
Serial.print(", ");
sendUpdate('z');
Serial.print(", ");
sendUpdate('u');
Serial.println();
}
// ---------------- Input Service routine for input from the Picaxe -------------
void getPicInput(void) {
byte stoInx;
char picChar;
byte fldCnt;
byte fldInx;
int msgInx;
bool mgActive = false;
while (picaxeLink.available()) {
picChar = picaxeLink.read();
if (picChar == '>') {
flds[fldInx].fldData[stoInx] = 0;
mgActive = false;
msgs[msgInx].updates++;
}
if ((picChar == '<') && !mgActive) {
picChar = picaxeLink.read(); // KeyCode Skip over the <
if ((picChar >= 'n') && (picChar <= 'z')) {
msgInx = getMsgInx[(picChar - 'n')];
keyCode = picChar; // Global flag
fldCnt = 0;
stoInx = 0;
mgActive = true;
}
picChar = picaxeLink.read(); //Don't write the keyCode
picChar = picaxeLink.read(); //Skip over the first comma
}
if (mgActive) {
fldInx = msgs[msgInx].fieldInx + fldCnt;
if ((picChar == ',') && (fldCnt < msgs[msgInx].fldNum)) {
fldCnt++;
stoInx = 0;
}
if (picChar != 44) {
flds[fldInx].fldData[stoInx++] = picChar;
if ((fldInx == 7) && (stoInx == 1)) {
flds[fldInx].fldData[stoInx++] = '.';
}
if ((fldInx == 9) && (stoInx == 2)) {
flds[fldInx].fldData[stoInx++] = '.';
}
if ((fldInx == 7) && (stoInx == 6)) {
dispMode = picChar;
}
}
}
}
}
void setup() {
pinMode(picRx, INPUT);
pinMode(RTS, INPUT);
Serial.begin(9600);
picaxeLink.begin(9600);
attachInterrupt(digitalPinToInterrupt(RTS), getPicInput, FALLING);
breakpoint = millis();
keyCode = 0;
dispMode = 49;
}
void loop() {
long nowTag;
long gap;
// getPicInput();
// ----------------------- Transmission control ------------------------
nowTag = millis();
gap = nowTag - breakpoint;
if (gap > 30000) { // 6 minutes
if (dispMode == 60) {
sendPage();
}
breakpoint = nowTag;
}
if ((dispMode == 49) && (keyCode >= 'n') && (keyCode <= 'z')) {
sendMessage(keyCode);
keyCode = 0;
}
delay(10);
}