WiFi Re-Transmitter Arduino Program

 Includes and  Defines

/* 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

// 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"};      //

 Transmit Function

// -------------------------------------- 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();

}

Picaxe Input

// ---------------- 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;

        }

      }

    }

  }

}

Setup and Loop


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);

}