Hello everybody. I have a lot of 5,000 square meters. It has a fence but easy to bypass. I would like to setup 4 invisible fences like a laser beam on each side. So small animals don't trigger it but a human will. Maybe using LoRa. I just need to trigger a relay in the house that would alert me that someone is entering the property. 4 individual small relay would be great so I know which side the subject is coming. What do you guys suggest to use for this project? Thank you.
Best I can think of over those distances is a bare steel (fence) wire , insulated from ground, similar to the setup used for electric fences.
In place of the HV, use a detector to monitor any ground reference.
Thank you bluejets. Definitely an option.
Why not use an electrial cattle fence to keep trespassers away?
Thanks Larry but I am looking more for a Arduino project idea. I love to Arduino.
A (red) laser beam is a poor choice. Too thin (insects could trigger it), and it's visible.
IR beam break detectors, up to 250m, are available off the shelf. And not too expensive.
Adding your own Arduino/wireless to it shouldn't be a big problem.
Leo..
Sure but I am more looking for an invisible discret Arduino base solution
Wawa now you are talking. Could you elaborate on that like a model number? Thank you
Sorry I just saw the link
Doesn't tell me what the perimeter dimensions are.
What are the side lengths you want to monitor?
Tom...
I have been working on this for years. The Large Property Perimeter Monitor. the large property is a gravel pit containing nothing of value. 1300 feet by 300 feet. the driveway is 400 feet long. if you put a sensor on the driveway entrance you get 20 seconds warning when the delivery truck enters the driveway, By the time they get to the front door you have had a whole minutes notice.
There are 2 major components of the LPPM;
the FSB - Field Service Box, and the Monitoring Station. Both use TTGO ESP32 modules. Both are constructed to operate in robust containers; you need fabrication skills.
this program is a good place to rob and pillage network, GPS, RTC, time and weather routines.
an aggravation in alarm systems: you know it works if it generates false positives. you do not know it does not work until you get an intruder who leaves evidence. you need some kind of "I'm alive" signal from the FSBs to have confidence in the system.. you need a heartbeat signal. I chose to design a weather station into the FSB
THIS IS A WORK IN PROGRESS.
The FSB:
it compiles and operates. the BME280 returns accurate weather data. The current project is to physically mount the FSB Module into the FSB, which is a complex device requiring a separate write up.
This particular FSB uses a PIR to trigger an ultrasonic sensor to ping and listen for an echo. if it returns an echo it is probably from a vehicle, not a coyote. when the weather goes sour I will build a mm wave RADAR FSB with an anemometer. The code is written, compiles, not tested.
This FSB Module uses a LILYGO T3 V 2.16 ESP32 module, a DS3231, a BME280, a PIR sensor and a JSN-SR04Tultrasonic sensor
/* Field Sensor Box X.025 Jun 03 2023 Port: ACM0
TTGO OLED WiFi V2.16: PIR^ WiFi^ NTP^ LoRa^ JSN-SR04T^ RTC^ BME280^ SD^
I2C Addresses (0x3c: OLED) (0x76: BME280) (0x68 RTC ) (0x57 EEPROM)
TTD: DS3231 Interrupt, SD card, test TEST_PIN, regulate packetSendTimer with FSBID */
#include "soc/soc.h" // Disable brownout problems
#include "soc/rtc_cntl_reg.h" // Disable brownout problems
#include <WiFi.h>
#include <Wire.h>
#include <SSD1306.h>
#include <SPI.h>
#include <SD.h>
#include <FS.h>
#include <LoRa.h>-
#include <time.h>
#include <RTClib.h>
RTC_DS3231 rtc;
int FSBID = 5; // unique ID per FSB
SPIClass * vspi = NULL;
SPIClass * hspi = NULL;
File dataFile; // filename variable for SD
char filename[] = "00000000.CSV"; // filename array for event logger
char logString[] = "000000000000"; // string for the event log
#define HAS_SDCARD 1
#define SDCARD_MISO 02
#define SDCARD_CS 13
#define SDCARD_SCLK 14
#define SDCARD_MOSI 15
bool getFilenameStatus = true;
#define I2C_SDA 21
#define I2C_SCL 22
#define BOARD_LED 25 // Set GPIOs for LED PIR Motion Sensor & JSNSR04T
#define RTC_INT_PIN 36 // the pin that is connected to SQW on the RTC
#define ALARM_TEST 39 // simulate alarm without sensors
const int motionSensor = 4; // PIR motion sensor pin
volatile byte alarmStatus = 0;
#define packetSendInterval 60 // Packet Send Interval in seconds
unsigned long currentMillis = 0; // start packet timer
unsigned long priorPacket = 0; // last time a packet was sent
unsigned long now = millis(); // Timer: Auxiliary variables
boolean startTimer = false;
// WiFi settings
String hostname = "FSB02_South"; // ID FSB over WiFi Here
const char* ssid = "yourNetworkName"; // "yourNetworkName";
const char* password = "yourNetworkPassword"; // "yourNetworkPassword";
const char* ntpServer = "us.pool.ntp.org";
const long gmtOffset_sec = 0;
const int daylightOffset_sec = 3600;
char OLEDDateTime[19];
#define ss 18 // LoRa-chip select
#define rst 23 // LoRa-reset
#define dio0 26 // LoRa Serial input
#define BAND 915E6 // 915E6 for North America
int readingID = 0; // packet counter
int counter = 0;
String LoRaPacket = "";
SSD1306 display(0x3c, I2C_SDA, I2C_SCL);
struct tm timeinfo;
//////////////////////////////////////////// RUN ONCE //////////////////////////////
void startLoRA() // stable
{
LoRa.setPins(ss, rst, dio0); //setup LoRa transceiver module
while (!LoRa.begin(BAND) && counter < 10)
{
Serial.print(".");
counter++;
delay(500);
}
if (counter == 10)
{
Serial.println("LoRav ");
Serial.println();
display.drawString(0, 24, "LoRav");
display.display();
}
else
{
Serial.println("LoRa^ ");
Serial.println();
display.drawString(0, 24, "LoRa^");
display.display();
}
}
void initWiFi()
{
WiFi.mode(WIFI_STA);
WiFi.setHostname(hostname.c_str());// define hostname
Serial.print("WiFi Scan start ");
int n = WiFi.scanNetworks();
Serial.print("WiFi Scan done ");
if (n == 0)
{
Serial.println("no networks found");
}
else
{
Serial.print(n);
Serial.println(" networks found");
Serial.println(" # | SSID | RSSI | CH | Encryption");
for (int i = 0; i < n; ++i)
{
Serial.printf("%2d", i + 1);
Serial.print(" | ");
Serial.printf("%-32.32s", WiFi.SSID(i).c_str());
Serial.print(" | ");
Serial.printf("%4d", WiFi.RSSI(i));
Serial.print(" | ");
Serial.printf("%2d", WiFi.channel(i));
Serial.print(" | ");
switch (WiFi.encryptionType(i))
{
case WIFI_AUTH_OPEN:
Serial.print("open");
break;
case WIFI_AUTH_WEP:
Serial.print("WEP");
break;
case WIFI_AUTH_WPA_PSK:
Serial.print("WPA");
break;
case WIFI_AUTH_WPA2_PSK:
Serial.print("WPA2");
break;
case WIFI_AUTH_WPA_WPA2_PSK:
Serial.print("WPA+WPA2");
break;
case WIFI_AUTH_WPA2_ENTERPRISE:
Serial.print("WPA2-EAP");
break;
default:
Serial.print("unknown");
}
Serial.println();
delay(10);
}
WiFi.scanDelete();
Serial.println();
}
/////////////////////////////////////////////////////////////////////////
Serial.print("Connecting to "); Serial.print(ssid); Serial.println(":");
while (!WiFi.begin(ssid, password));
{
counter++;
Serial.print("WiFiv "); Serial.print(counter); Serial.print(" ");
delay(1000);
if (counter == 10)
{
counter = 0;
Serial.println();
Serial.print("WiFi AP "); Serial.print(ssid); Serial.println(" not available ");
return;
}
Serial.println();
}
}
void WiFiStationGotIP(WiFiEvent_t event, WiFiEventInfo_t info)
{
Serial.print("WiFiStationGotIP: ");
Serial.println(IPAddress(info.got_ip.ip_info.ip.addr));
return;
}
void initTime(String timezone) // stable
{
Serial.println("Setting NTP time");
Serial.println();
configTime(0, 0, "us.pool.ntp.org"); // connect to NTP server, with 0 TZ offset
while (!getLocalTime(&timeinfo) && counter < 10)
{
counter++;
Serial.print("NTPv "); Serial.print(counter); Serial.print(" ");
display.drawString(88, 24, "NTPv ");
}
if (counter == 10)
{
counter = 0;
DateTime now = rtc.now();
Serial.println(("NTPv: set time from RTC"));
printLocalTime();
Serial.println();
}
if (getLocalTime(&timeinfo))
{
display.drawString(88, 24, "NTP^ ");
display.display();
Serial.println();
Serial.println("NTP^");
Serial.println();
setTimezone(timezone);
Serial.print( "NTPinitTime: ");
printLocalTime();
upDateOLEDDisplay();
}
}
void setTimezone(String timezone) // stable
{
Serial.printf("Setting Timezone to %s\n", timezone.c_str());
setenv("TZ", timezone.c_str(), 1); // Now adjust the TZ to your local time
tzset();
}
void setRTC()
{
Serial.print("setRTC(); ");
getLocalTime(&timeinfo);
rtc.adjust(DateTime(timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday,
timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec));
DateTime now = rtc.now();
Serial.print(now.year(), DEC);
Serial.print('/');
Serial.print(now.month(), DEC);
Serial.print('/');
Serial.print(now.day(), DEC);
Serial.print('/');
Serial.print(now.hour(), DEC);
Serial.print(':');
Serial.print(now.minute(), DEC);
Serial.print(':');
Serial.print(now.second(), DEC);
Serial.println();
char RTCString[] = "0000000000000"; // string for the event log
sprintf(RTCString, "%04d %02d %02d %02d %02d %02d", now.year(), DEC, now.month(), DEC,
now.day(), DEC, now.hour(), DEC, now.minute(), DEC, now.second(), DEC);
display.drawString(0, 48, RTCString);
display.display();
}
/////////////////////////////////// CALLED FUNCTIONS ///////////////////////////////
void printLocalTime() // stable
{
if (!getLocalTime(&timeinfo))
{
Serial.println("Failed to obtain NTP time");
return;
}
Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S zone %Z %z ");// Friday, April 15 2022 19:18:51 zone MDT -0600
Serial.println();
}
void afterMidnight()
{
getFilenameStatus = true; // isr for midnight alarm
}
void dateUpdate()
{
if (getFilenameStatus == true );
{
readingID = 0;
getFilename();
getFilenameStatus = 0;
rtc.clearAlarm(1);
Serial.println("dateUpdate ()Alarm(1) cleared");
Serial.println();
}
}
void IRAM_ATTR setALARM_TEST()
{
alarmStatus = FSBID;
}
void IRAM_ATTR detectMovement() // stable: PIR motion detect and latch
{
alarmStatus = FSBID;
}
void alarmHandler()
{
if (alarmStatus == FSBID)
{
alarmTasks();
}
}
void alarmTasks()
{
{
Serial.print("!!!!!!!!!! alarmStatus == ");
Serial.print(FSBID);
Serial.println(" !!!!!!!!!!");
sendLoRaPacket();
logEvent();
alarmStatus = 0;
Serial.println("alarmStatus = 0");
Serial.println( "iiiii Alarm-Triggered Packet Sent iiiii");
Serial.println();
digitalWrite(BOARD_LED, LOW);
}
}
void packetSendTimer() // send LoRa packets at intervals set in packetSendInterval
{
now = millis(); // start packetSendTimer
if (startTimer && (now - priorPacket >= (packetSendInterval * 1000))) // && second = FSBID )
{
currentMillis = millis();
sendLoRaPacket();
Serial.println("------- Timed LoRa Packet Sent -------");
Serial.println();
}
}
void sendLoRaPacket() // stable
{
Serial.print("+++++++ FSB "); Serial.print(FSBID); Serial.println(" LoRa Packet Send +++++++");
LoRaPacket = String(FSBID) + "#" + (readingID) + "/" + (alarmStatus)
// anemometer + "!" + (bme.readTemperature()) + "C" +
// data send (bme.readPressure()) + "P" + (bme.readHumidity()) + "%"
;
readingID++;
Serial.print("Sending packet: ");
LoRa.beginPacket();
LoRa.print(LoRaPacket);
LoRa.endPacket();
Serial.println(LoRaPacket);
now = millis();
priorPacket = (now);
startTimer = true;
}
void getFilename()
{
if ( getFilenameStatus == true )
{
getLocalTime(&timeinfo);
sprintf( filename, "%04d%02d%02d", timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday);
Serial.print( "filename: ");
Serial.println( filename );
Serial.println();
display.drawString(0, 40, filename );
display.display();
getFilenameStatus = false;
}
}
void logEvent()
{
getLocalTime(&timeinfo);
{
File dataFile = SD.open(filename, FILE_WRITE);
if (dataFile) // if the file is available, write to it:
{
display.drawString(62, 16, "SDo");
sprintf(logString, "%02d,%02d,%02d,%02d", FSBID, timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);
dataFile.println(logString);
dataFile.close();
Serial.print("logstring stored: ");
Serial.println(logString);
Serial.println();
display.drawString(62, 16, "SD*");
display.display();
}
}
}
void upDateOLEDDisplay()
{
getLocalTime(&timeinfo);
sprintf(OLEDDateTime, "%04d/%02d/%02d %02d:%02d:%02d", timeinfo.tm_year + 1900,
timeinfo.tm_mon + 1, timeinfo.tm_mday, timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec, " ");
display.drawString(0, 32, String(OLEDDateTime));
display.display();
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void setup()
{
Serial.begin(115200);
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detector
Serial.println();
Serial.print("Field Sensor Box ");
Serial.print(FSBID);
Serial.print(" V X.025 ");
Serial.println("PIR^ WiFi^ NTP^ LoRa^ Anemometer^ RADAR^ RTC^ SDv "); // current status
Serial.println();
Wire.begin(I2C_SCL, I2C_SDA);
pinMode(I2C_SCL, OUTPUT);
Serial.println ("I2C scanner. Scanning ...");
byte count = 0;
for (byte i = 8; i < 120; i++)
{
Wire.beginTransmission (i);
if (Wire.endTransmission () == 0)
{
Serial.print ("Found address: ");
Serial.print (i, DEC);
Serial.print (" (0x");
Serial.print (i, HEX);
Serial.println (")");
count++;
delay (1);
} // end of good response
} // end of for loop
Serial.print ("Done.");
Serial.print ("Found ");
Serial.print (count, DEC);
Serial.println(" device(s).");
Serial.println();
display.init();
display.clear();
display.flipScreenVertically();
display.drawString(0, 0, "FSB Version X.025");
display.drawString(0, 8, "LPPM FSB02 South");
// increment Y by 8 to shift down one line
// increment X by ( 4 *( of characters on previous line +1 )) to append on same line
vspi = new SPIClass(VSPI);
vspi->begin(SCK, MISO, MOSI, SS); // loRa SPI
/*loRa_SCLK 14 loRa_MISO 19 loRa_MOSI 27 loRa_SS 18 */
SPIClass sdSPI(HSPI);
sdSPI.begin(SDCARD_SCLK, SDCARD_MISO, SDCARD_MOSI, SDCARD_CS);
if (!SD.begin(SDCARD_CS, sdSPI)) // digitalWrite(SDCARD_CS, LOW);
{
Serial.print("SDv ");
display.drawString(40, 16, "SDv");
}
else
{
display.drawString(40, 16, "SD^");
uint32_t cardSize = SD.cardSize() / (1024 * 1024);
char outputString[2];
itoa(cardSize, outputString, 10 );
Serial.print("SD^ ");
Serial.print(cardSize);
Serial.print(" gb ");
display.drawString(60, 16, outputString);
}
// if (!bme.begin(0x76))
// {
// Serial.println("BME280v ");
// display.drawString(36, 24, "BME280v" );
// }
// else
// {
// Serial.println("BME280^ ");
// display.drawString(36, 24, "BME280^" );
// printBME();
initWiFi();
WiFi.onEvent(WiFiStationGotIP, SYSTEM_EVENT_STA_GOT_IP);
Serial.println("Delay(10000);"); delay(10000);
initTime("MST7MDT,M3.2.0,M11.1.0"); // MST https://leo.leung.xyz/wiki/Timezone
if (!rtc.begin())
{
Serial.println("RTCv ");
Serial.println();
display.drawString(88, 16, "RTCv" );
}
else
{
Serial.println("RTC^ ");
Serial.println();
display.drawString(88, 16, "RTC^" );
rtc.disable32K(); // disable the 32K Pin
pinMode(RTC_INT_PIN, INPUT); // attach an interrupt to the alarm
attachInterrupt(digitalPinToInterrupt(RTC_INT_PIN), afterMidnight, FALLING);
rtc.clearAlarm(1); // reset alarm 1, 2 flag on reboot/recompile
rtc.clearAlarm(2);
rtc.disableAlarm(2); // turn off alarm 2 at reboot
rtc.writeSqwPinMode(DS3231_OFF); // stop signals at SQW Pin
if (!rtc.setAlarm1(DateTime(0, 0, 0, 0, 5, 0), DS3231_A1_Hour))
// this sets the alarm to trigger at midnight
{
Serial.println("Error, alarm was not set!");
}
else
{
Serial.println("Alarm set to trigger at midnight");
Serial.println();
}
setRTC();
}
if (WiFi.begin(ssid, password))
{
display.drawString(0, 16, ( ssid ));
display.drawString(32, 16, "^");
}
else
{
display.drawString(0, 16, "WiFiv" );
}
display.display();
// Motion Sensor Setup // stable
// pinMode(motionSensor, INPUT);
// attachInterrupt(digitalPinToInterrupt(motionSensor), detectMovement, FALLING);
// pinMode(TRIGGER, OUTPUT);
// pinMode(SENSOR, INPUT);
pinMode(ALARM_TEST, INPUT);
attachInterrupt(digitalPinToInterrupt(ALARM_TEST), setALARM_TEST, FALLING);
pinMode(BOARD_LED, OUTPUT);
digitalWrite(BOARD_LED, LOW);
startLoRA();
getFilenameStatus = true;
dateUpdate();
now = millis();
priorPacket = (now);
startTimer = true;
Serial.println( "Setup Complete. Start of watch");
Serial.println();
}
///////////////////////////////////////////////////////////////////////////////////////
void loop()
{
alarmHandler(); // send alarm packet as required
packetSendTimer(); // send weather data at timed intervals
}
The Monitoring Staiton
When a project of this size reaches a late stage of completion you must mount all the components so you can stabilize the wiring and connections. Programming becomes a lower priority than fabrication. this device is in the process of physical construction.
This device uses a TTGO T Beam ESP32 with LoRa, GPS, and WiFi built in. External DS3231 RTC, SD card, and a YX5300 sound module.
It compiles, all functions work, but time sync via GPS if NTP fails is untested.
The Monitoring Station Code
// T Beam Lora Gateway for FSBs 009H port /dev/ttyACMO 20230703
// TTD: MOUNT ALL, RTC_INT, TEST_PIN, GPS,
// GPS set time, add counters to bail on invalid GPS or LoRa
// readParseGPS(), dateUpdate(), setTimezone
#include "soc/soc.h" // Disable brownout problems
#include "soc/rtc_cntl_reg.h" // Disable brownout problems
#include <WiFi.h>
#include <Wire.h>
#include <SPI.h>
#include <FS.h>
#include <SD.h>
#include <LoRa.h> // Sandeepmistry
#include <time.h> // ESP32Time
#include <TinyGPS++.h>
#include <HardwareSerial.h>
#include "RTClib.h"
RTC_DS3231 rtc;
#define RTC_INT_PIN 36 // the pin that is connected to SQW on the RTC
#define TEST_PIN 38 // middle pushbutton as system test
volatile bool setFSBtime;
int FSBID = 0;
SPIClass * vspi = NULL; // VSPI LoRa
SPIClass * hspi = NULL; // HSPI SD
File dataFile; // filename variable for SD
char filename[] = "00000000.CSV"; // filename array for datalogger
char logString[] = "000000000000"; // string for the event log; may be one char too long.
#define HAS_SDCARD 1
#define SDCARD_MISO 04 // RED 02
#define SDCARD_CS 13 // YLO 13
#define SDCARD_SCLK 14 // ORG 14
#define SDCARD_MOSI 15 // YLO 15
bool getFilenameStatus = true;
#define I2C_SDA 21 // GRA 21
#define I2C_SCL 22 // VIO 22
#define ss 18 // VSPI CLK
#define rst 23 // VSPI MOSI
#define dio0 26 // LoRA IO
#define BAND 915E6 // 915E6 for North America
HardwareSerial GPSSerial(2);
#define GPS_RX_PIN 34
#define GPS_TX_PIN 12
TinyGPSPlus gps;
HardwareSerial SoundSerial(1); // YX-5300
#define Sound_RX_PIN -1 // Not required
#define Sound_TX_PIN 25 // WHT TO YX-5300 RX
int sw = 0;
#define CMD_SEL_DEV 0X09
#define DEV_TF 0X02
#define CMD_SET_VOLUME 0X06
#define CMD_PLAY_W_INDEX 0X03
#define CMD_PLAY_FOLDER_FILE 0X0f // 0x7E 06 0F 00 01 02 EF;( play folder 01/002.wav )
static unsigned int Send_buf[8] = {0}; // char string for YX-5300 commands
int folderArray[] = { 0, 1, 2, 3, 4, 5, 5, 5, 6, 6, 6, 6, 12, 13, 14,};
int filesPerFolderArray[] = {15, 20, 30, 4, 6, 9, 9, 9, 25, 25, 25, 25, 29, 255, 74,};
/////////////////////////// Set these to suit your system and location /////////////////////
String hostname = "T Beam Gateway 09H2]";
const char* ssid = "ad5mb"; // "yourNetworkName";
const char* password = "sansuiau517"; // "yourNetworkPassword";
const char* ntpServer = "us.pool.ntp.org";
const char* timezone = "MST7MDT,M3.2.0,M11.1.0"; // https://leo.leung.xyz/wiki/Timezone
int localElevation = 1421; // elevation in meters
/////////////////////////////////////////////////////////////////////////////////////////
struct tm timeinfo;
int counter = 0; // Initialize variables to get and save LoRa data
int LoRarssi = 0;
float temperature_C; // Initialize variables to get and save Weather data
float temperature_F;
float pressure_hPa;
float Pressure;
float pressure_inHg;
float Humidity;
float seaLevelPressure_hPa;
float seaLevelPressure_inHg;
float dewpoint_F;
float dewpoint_C;
String fsbid;
String loRaMessage;
String alarmStatus;
String temperature;
String pressure;
String humidity;
String readingID;
//////////////////////////////////// RUN ONCE //////////////////////////////////////
void initWiFi()
{
WiFi.mode(WIFI_STA);
WiFi.setHostname(hostname.c_str());// define hostname
Serial.print("WiFi Scan start ");
int n = WiFi.scanNetworks();
Serial.print("WiFi Scan done ");
if (n == 0)
{
Serial.println("no networks found");
}
else
{
Serial.print(n);
Serial.println(" networks found");
Serial.println(" # | SSID | RSSI | CH | Encryption");
for (int i = 0; i < n; ++i)
{
Serial.printf("%2d", i + 1);
Serial.print(" | ");
Serial.printf("%-32.32s", WiFi.SSID(i).c_str());
Serial.print(" | ");
Serial.printf("%4d", WiFi.RSSI(i));
Serial.print(" | ");
Serial.printf("%2d", WiFi.channel(i));
Serial.print(" | ");
switch (WiFi.encryptionType(i))
{
case WIFI_AUTH_OPEN:
Serial.print("open");
break;
case WIFI_AUTH_WEP:
Serial.print("WEP");
break;
case WIFI_AUTH_WPA_PSK:
Serial.print("WPA");
break;
case WIFI_AUTH_WPA2_PSK:
Serial.print("WPA2");
break;
case WIFI_AUTH_WPA_WPA2_PSK:
Serial.print("WPA+WPA2");
break;
case WIFI_AUTH_WPA2_ENTERPRISE:
Serial.print("WPA2-EAP");
break;
default:
Serial.print("unknown");
}
Serial.println();
delay(10);
}
WiFi.scanDelete();
Serial.println();
}
/////////////////////////////////////////////////////////////////////////
Serial.print("Connecting to "); Serial.print(ssid); Serial.println(":");
while (!WiFi.begin(ssid, password));
{
counter++;
Serial.print("WiFiv "); Serial.print(counter); Serial.print(F(" "));
delay(1000);
if (counter == 10)
{
counter = 0;
Serial.println();
Serial.print("WiFi AP "); Serial.print(ssid); Serial.println(" not available ");
return;
}
Serial.println();
}
}
void WiFiStationGotIP(WiFiEvent_t event, WiFiEventInfo_t info)
{
Serial.print("WiFiStationGotIP: ");
Serial.println(IPAddress(info.got_ip.ip_info.ip.addr));
return;
}
void initTime(String timezone) // stable
{
if (WiFi.status() == WL_CONNECTED)
{
Serial.println("initTime getting NTP time: ");
while (!getLocalTime(&timeinfo) && counter < 10)
{
configTime(0, 0, ntpServer); // connect to NTP server, with 0 TZ offset
counter++;
Serial.print("NTPv "); Serial.print(counter); Serial.print(F(" "));
}
Serial.println();
if (getLocalTime(&timeinfo))
{
setTimezone(timezone);
Serial.print("LocalTime: ");
printLocalTime();
}
}
else if (!getLocalTime(&timeinfo))
{
Serial.println("NTPv: readParseGPS"); Serial.println();
readParseGPS();
}
}
void setTimezone(String timezone) // stable
{
Serial.printf("Setting Timezone to %s\n", timezone.c_str());
setenv("TZ", timezone.c_str(), 1); // adjust the TZ. Clock settings are adjusted to show the new local time
tzset();
return;
}
void startLoRA() // stable 1 day = 86,400,000 milliseconds
{
LoRa.setPins(ss, rst, dio0); // Initialize LoRa transceiver module
while (!LoRa.begin(BAND) && counter < 10)
{
Serial.print("LoRav ");
counter++;
delay(500);
}
if (counter == 10)
{
counter = 0;
Serial.print("LoRav ");
Serial.println();
}
else
{
Serial.println("LoRa^");
Serial.println();
}
}
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> CALLED FUNCTIONS <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<//
void setRTC()
{
Serial.print("setRTC(); ");
getLocalTime(&timeinfo);
rtc.adjust(DateTime(timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday,
timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec));
DateTime now = rtc.now();
Serial.print(now.year(), DEC);
Serial.print('/');
Serial.print(now.month(), DEC);
Serial.print('/');
Serial.print(now.day(), DEC);
Serial.print('/');
Serial.print(now.hour(), DEC);
Serial.print(':');
Serial.print(now.minute(), DEC);
Serial.print(':');
Serial.print(now.second(), DEC);
Serial.println();
}
void printLocalTime() // stable
{
if (!getLocalTime(&timeinfo))
{
Serial.println("Failed to obtain NTP time");
}
Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S Timezone %Z %z ");// Friday, April 15 2022 19:18:51 zone MDT -0600
Serial.println();
}
void readParseGPS()
{
if (GPSSerial.available())
{
while (GPSSerial.available() > 0)
gps.encode(GPSSerial.read());
}
if (gps.date.isUpdated())
{
Serial.print(F("GPS Year="));
Serial.print(gps.date.year()); Serial.print(F(" "));
Serial.print(F("GPS Month="));
Serial.print(gps.date.month()); Serial.print(F(" "));
Serial.print(F("GPS Day="));
Serial.println(gps.date.day());
}
if (gps.time.isUpdated())
{
Serial.print(F("Hour="));
Serial.print(gps.time.hour()); Serial.print(F(" "));
Serial.print(F("Minute="));
Serial.print(gps.time.minute()); Serial.print(F(" "));
Serial.print(F("Second="));
Serial.println(gps.time.second());
timeinfo.tm_sec = gps.time.second();
timeinfo.tm_min = gps.time.minute();
timeinfo.tm_hour = gps.time.hour();
timeinfo.tm_mday = gps.date.day();
timeinfo.tm_mon = gps.date.month();
timeinfo.tm_year = gps.date.year();
setTimezone(timezone);
printLocalTime();
Serial.println("Set RTC to GPS");
setRTC();
}
}
//void IRAM_ATTR testPinSet()
//{
// sw = (6); = (1); //random(1, 15);
//}
void setRemotetime()
{
setFSBtime = true; // isr for midnight alarm
}
void dateUpdate() // Midnight FSB time Update
{
if (setFSBtime == true )
{
setFSBtime = false;
rtc.clearAlarm(1);
}
if (gps.time.isValid()) // first couple of times through the loop, gps not yet available
{
LoRa.beginPacket(); // send LoRa packet
LoRa.printf("Time: %.2d:%.2d:%.2d\n", gps.time.second(), gps.time.minute(), gps.time.hour());
LoRa.endPacket();
Serial.println("FSBs set to GPS time");
Serial.println("Midnight FSB time Update^ Alarm(1) cleared. setFSB time cleared");
}
}
////////////////////////////////////////////////////////////////////////////////////
void readParseLoRa() // Read LoRa packet and get the sensor readings
{
int packetSize = LoRa.parsePacket();
if (packetSize)
{
Serial.print("Lora packet received: ");
Serial.print(packetSize);
Serial.println(" characters");
while (LoRa.available()) // Read packet
{
String LoRaData = LoRa.readString();
Serial.print(LoRaData);
int pos1 = LoRaData.indexOf('@');
int pos2 = LoRaData.indexOf('/');
int pos3 = LoRaData.indexOf('!');
int pos4 = LoRaData.indexOf('F');
int pos5 = LoRaData.indexOf('P');
int pos6 = LoRaData.indexOf('%');
fsbid = LoRaData.substring(0, pos1); // Get FSB ID
readingID = LoRaData.substring(pos1 + 1, pos2); // Get readingID
alarmStatus = LoRaData.substring(pos2 + 1, pos3); // Get alarmStatus
temperature = LoRaData.substring(pos3 + 1, pos4); // Get temperature
pressure = LoRaData.substring(pos4 + 1, pos5); // Get pressure
humidity = LoRaData.substring(pos5 + 1, LoRaData.length()); // Get humidity
}
LoRarssi = LoRa.packetRssi(); // Get RSSI
Serial.print(" LoRa RSSI: ");
Serial.println(LoRarssi);
Serial.print("alarmStatus = ");
Serial.print(alarmStatus);
Serial.print(" alarmStatus.toInt() = ");
Serial.println(alarmStatus.toInt());
Serial.println();
if ( alarmStatus.toInt() != 0 )
{
Serial.print( "Alarm Triggered > "); Serial.print(alarmStatus); Serial.print(F(" "));
int sw = alarmStatus.toInt();
Serial.print("sw = "); Serial.println(sw); Serial.println();
alarmHandler(sw);
}
BME_280_conversions();
}
}
////////////////////////////////////////////////////////////////////////////////////
void alarmHandler(int sw)
{
logEvent(); // log event to SD card
playWAV(sw); // play appropriate sound file
}
void getFilename() //
{
if ( getFilenameStatus == true )
{
getLocalTime(&timeinfo);
sprintf( filename, "%04d%02d%02d", timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday );
Serial.print( "filename(): ");
Serial.println(filename);
Serial.println();
getFilenameStatus = false;
}
}
void logEvent()
{
getLocalTime(&timeinfo);
{
File dataFile = SD.open(filename, FILE_WRITE);
if (dataFile) // if the file is available, write to it:
{
sprintf(logString, "%02d,%02d,%02d,%02d", FSBID, timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);
dataFile.println(logString);
dataFile.close();
Serial.print( "Event log string saved: ");
Serial.println( logString );
}
}
}
////////////////////////////////////
// YX5300 sound module functions ///
////////////////////////////////////
void playWAV (int sw)
{
int folder = folderArray[sw]; int filesPerFolder = filesPerFolderArray[sw];
randomFile(folder, filesPerFolder);
}
void randomFile(int folder, int filesPerFolder)
{
randomSeed(timeinfo.tm_sec);
sendCommand(CMD_PLAY_FOLDER_FILE, 0x0F0000 + (folder * 256) + (random(1, filesPerFolder)));
}
void sendCommand(uint8_t command, int16_t dat)
{
Send_buf[0] = 0x7e; // starting byte
Send_buf[1] = 0xff; // version
Send_buf[2] = 0x06 ; // the number of bytes of the command without starting byte and ending byte
Send_buf[3] = command; //
Send_buf[4] = 0x00; // 0x00 = no feedback, 0x01 = feedback
Send_buf[5] = (int8_t)(dat >> 8); // data high byte
Send_buf[6] = (int8_t)(dat); // data low byte
Send_buf[7] = 0xef; // ending byte
for (uint8_t i = 0; i < 8; i++)
{
SoundSerial.write(Send_buf[i]);
Serial.print(Send_buf[i], HEX);
}
Serial.println();
}
void BME_280_conversions()
{
temperature_C = (temperature.toInt());
Serial.print("Temperature C = ");
Serial.print(temperature_C);
Serial.println(" *C");
temperature_F = ((1.8 * temperature_C) + 32); // Convert Celcius temperature to Fahrenheit
Serial.print("Temperature F = ");
Serial.print(temperature_F);
Serial.println( " F" );
Pressure = (pressure.toInt());
pressure_hPa = ((Pressure / 100.0F));
Serial.print("Pressure hPa = ");
Serial.print(pressure_hPa);
Serial.println(" hPa" );
pressure_inHg = ((pressure_hPa / 100.0F) / 33.864); // Convert hectoPascals to inHg
Serial.print("Pressure inHG = ");
Serial.print(pressure_inHg);
Serial.println( " inHG");
Humidity = (humidity.toInt());
Serial.print( "Humidity = ");
Serial.print(Humidity);
Serial.println(" %");
seaLevelPressure_hPa = pressure_hPa / pow(1.0 - 0.0065 * localElevation / (temperature_C + 273.15), 5.255);
Serial.print( "Sea Level Pressure hPa = " );
Serial.print( seaLevelPressure_hPa );
Serial.println( " hPa");
seaLevelPressure_inHg = seaLevelPressure_hPa / 33.864;
Serial.print( "Sea Level Pressure inHg = " );
Serial.print( seaLevelPressure_inHg );
Serial.println( " inHg");
dewpoint_F = 243.04 * (log(Humidity / 100) + ((17.625 * temperature_F) / (243.04 + temperature_F))) / (17.625 - log(Humidity / 100) - ((17.625 * temperature_F) / (243.04 + temperature_F)));
Serial.print("Dewpoint = ");
Serial.print(dewpoint_F);
Serial.println(" F");
dewpoint_C = ((temperature_F - 32) / 1.8 ); // Convert Fahrenheit temperature to Celcius
Serial.print("Dewpoint = ");
Serial.print(dewpoint_C);
Serial.println(" C");
Serial.println();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void setup()
{
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detector
Serial.begin(115200);
Serial.println();
Serial.println ("T Beam Gateway V009H"); // current status
// pinMode(TEST_PIN, INPUT);
// attachInterrupt(digitalPinToInterrupt(TEST_PIN), testPinSet, FALLING);
GPSSerial.begin(9600, SERIAL_8N1, GPS_RX_PIN, GPS_TX_PIN);
pinMode(GPS_TX_PIN, OUTPUT); // TO GPS RX
pinMode(GPS_RX_PIN, INPUT); // FROM GPS TX
SoundSerial.begin(9600, SERIAL_8N1, Sound_RX_PIN, Sound_TX_PIN);
pinMode(Sound_TX_PIN, OUTPUT); // TO YX-5300 RX
sendCommand(CMD_SEL_DEV, DEV_TF); // Select the YX-5300 TF card
delay(200);
sendCommand(CMD_SET_VOLUME, 0x06001e); // set volume to mid range
delay(200);
randomSeed(timeinfo.tm_sec);
sendCommand(CMD_PLAY_W_INDEX, 0x030000 + random(1, 15)); // play on reset 1 of 15 files in TF card root
Serial.println();
vspi = new SPIClass(VSPI);
vspi->begin(SCK, MISO, MOSI, SS); // LoRa SPI
//loRa_SCLK 5 loRa_MISO 19 loRa_MOSI 27 loRa_SS 18
pinMode(SS, OUTPUT);
pinMode(SCK, OUTPUT);
pinMode(MOSI, OUTPUT);
pinMode(MISO, INPUT);
hspi = new SPIClass(HSPI);
pinMode(SDCARD_CS, OUTPUT);
hspi->begin(SDCARD_SCLK, SDCARD_MISO, SDCARD_MOSI, SDCARD_CS);
//SDCARD_SCLK 14 SDCARD_MISO 02 SDCARD_MOSI 15 SDCARD_CS 13
if (!SD.begin(SDCARD_CS))
{
Serial.println("SDv ");
Serial.println();
}
if (SD.begin())
{
Serial.println("SD^ ");
Serial.println();
uint32_t cardSize = SD.cardSize() / (1024 * 1024);
char outputString[2];
itoa(cardSize, outputString, 10 );
Serial.print("Card Size = ^ ");
Serial.print(cardSize);
Serial.print(" mb ");
}
Wire.begin(I2C_SCL, I2C_SDA);
Serial.println ("I2C scanner. Scanning ...");
byte count = 0;
for (byte i = 8; i < 120; i++)
{
Wire.beginTransmission (i);
if (Wire.endTransmission () == 0)
{
Serial.print ("Found address: ");
Serial.print (i, DEC);
Serial.print (" (0x");
Serial.print (i, HEX);
Serial.println (")");
count++;
delay (1);
} // end of good response
} // end of for loop
Serial.print ("Done.");
Serial.print ("Found ");
Serial.print (count, DEC);
Serial.println(" device(s).");
Serial.println();
if (!rtc.begin())
{
Serial.println("RTCv");
Serial.println();
}
else if (rtc.begin())
{
Serial.println("rtc.begin");
rtc.disable32K(); // disable the 32K Pin
pinMode(RTC_INT_PIN, INPUT); // attach an interrupt to the alarm
attachInterrupt(digitalPinToInterrupt(RTC_INT_PIN), setRemotetime, FALLING);
rtc.clearAlarm(1); // reset alarm 1, 2 flag on reboot & recompile
rtc.clearAlarm(2);
rtc.disableAlarm(2); // turn off alarm 2 at reboot
rtc.writeSqwPinMode(DS3231_OFF); // stop signals at SQW Pin
rtc.setAlarm1(DateTime(0, 0, 0, 0, 1, 0), DS3231_A1_Hour);
Serial.println("Alarm(1) set to trigger at midnight");
Serial.println();
}
initWiFi();
WiFi.onEvent(WiFiStationGotIP, SYSTEM_EVENT_STA_GOT_IP);
Serial.println("Delay(10000);"); delay(10000);
initTime(timezone); // MST https://leo.leung.xyz/wiki/Timezone
setRTC();
getFilenameStatus = true;
getFilename();
dateUpdate();
startLoRA();
Serial.println("Setup complete. Monitoring ");
Serial.println();
}
void loop()
{
readParseLoRa();
dateUpdate();
}
Geek Emeritus Thank you so much. I will study your project!
looking back, I see I forgot to mention something major:
this was intended to be a doorbell. it grew into a datalogging perimeter monitor with a synced clock. that's called mission creep.
the sound card is a major component. the sound card has 15 folders. these folders contain sounds harvested from the internet. being a wheezy old geezer, I mainly used popular entertainment from the 60s and 70s as a source
the sound module is in the Monitoring Station and feeds an external amplifier. Line 62 of the code is an array that associates a particular FSB with a folder. folder 1 contains bells, gongs, and chimes that ring for 20 seconds. folder 2 contains things that ring twice. folder 3 contains things that make 3 sounds. these are non obnoxious sounds that alert me that people are politely coming in through the proper channel. folders 5 and 6 are annoying loud sounds for things that require a response in force. you will note that there are mulitiple instances that induce a sound from folders 5 and 6
line 63 contains an array of how many files are in each folder. I have determined that you need to listen to the sound files while the TV is on, to determine that the sound can be heard in a noisy environment. when you do that, and eliminate a few files, you have to adjust the files per folder array to match.
folders 12 and 13 are the doorbell. folder 13 is innocuous sounds like tigers and Tarzan. folder 12 is things that people who know me would appreciate, movie quotes, Daffy Duck and Lurch and Chewbacca.
the sound module playback routine selects random sounds from these folders. you may have to put up with people pushing the button over and over to see how long it takes to repeat.
This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.