Added particle project
This commit is contained in:
58
.gitignore
vendored
58
.gitignore
vendored
@ -29,4 +29,60 @@ logs/
|
|||||||
|
|
||||||
# Secrets
|
# Secrets
|
||||||
secrets/
|
secrets/
|
||||||
certificates/
|
certificates/
|
||||||
|
|
||||||
|
# Key files
|
||||||
|
*.der
|
||||||
|
*.pem
|
||||||
|
|
||||||
|
# Ignore build results and bundles
|
||||||
|
*.bin
|
||||||
|
*.zip
|
||||||
|
[Dd]ebug/
|
||||||
|
[Dd]ebugPublic/
|
||||||
|
[Rr]elease/
|
||||||
|
[Rr]eleases/
|
||||||
|
[Bb]in/
|
||||||
|
[Oo]bj/
|
||||||
|
[Ll]og/
|
||||||
|
[Ll]ogs/
|
||||||
|
target/*
|
||||||
|
|
||||||
|
# Platform-specific settings
|
||||||
|
.DS_Store
|
||||||
|
*.crc_block
|
||||||
|
*.no_crc
|
||||||
|
|
||||||
|
# VisualStudioCode
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/settings.json
|
||||||
|
!.vscode/tasks.json
|
||||||
|
!.vscode/launch.json
|
||||||
|
!.vscode/extensions.json
|
||||||
|
*.code-workspace
|
||||||
|
|
||||||
|
# Ignore all local history of files
|
||||||
|
**/.history
|
||||||
|
|
||||||
|
# Windows
|
||||||
|
Thumbs.db
|
||||||
|
*.stackdump
|
||||||
|
[Dd]esktop.ini
|
||||||
|
|
||||||
|
# C Prerequisites
|
||||||
|
*.d
|
||||||
|
|
||||||
|
# C Object files
|
||||||
|
*.o
|
||||||
|
*.ko
|
||||||
|
*.obj
|
||||||
|
*.elf
|
||||||
|
|
||||||
|
# C Linker output
|
||||||
|
*.map
|
||||||
|
|
||||||
|
# C Debug files
|
||||||
|
*.dSYM/
|
||||||
|
*.su
|
||||||
|
*.idb
|
||||||
|
*.pdb
|
||||||
|
|||||||
37
Particle/SecurityMonitor/.github/workflows/main.yaml
vendored
Normal file
37
Particle/SecurityMonitor/.github/workflows/main.yaml
vendored
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
# Particle Compile Action Workflow
|
||||||
|
# This workflow uses the Particle compile-action to compile Particle application firmware.
|
||||||
|
# Make sure to set the particle-platform-name for your project.
|
||||||
|
# For complete documentation, please refer to https://github.com/particle-iot/compile-action
|
||||||
|
|
||||||
|
name: Particle Compile
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
compile:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout Repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
# Particle Compile Action
|
||||||
|
- name: Compile Firmware
|
||||||
|
id: compile
|
||||||
|
uses: particle-iot/compile-action@v1
|
||||||
|
with:
|
||||||
|
# Set the particle-platform-name to the platform you're targeting.
|
||||||
|
# Allowed values: core, photon, p1, electron, argon, boron, xenon, esomx, bsom, b5som, tracker, trackerm, p2, msom
|
||||||
|
particle-platform-name: 'p2'
|
||||||
|
|
||||||
|
# Optional: Upload compiled firmware as an artifact on GitHub.
|
||||||
|
- name: Upload Firmware as Artifact
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: firmware-artifact
|
||||||
|
path: |
|
||||||
|
${{ steps.compile.outputs.firmware-path }}
|
||||||
|
${{ steps.compile.outputs.target-path }}
|
||||||
55
Particle/SecurityMonitor/.gitignore
vendored
Normal file
55
Particle/SecurityMonitor/.gitignore
vendored
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
# Key files
|
||||||
|
*.der
|
||||||
|
*.pem
|
||||||
|
|
||||||
|
# Ignore build results and bundles
|
||||||
|
*.bin
|
||||||
|
*.zip
|
||||||
|
[Dd]ebug/
|
||||||
|
[Dd]ebugPublic/
|
||||||
|
[Rr]elease/
|
||||||
|
[Rr]eleases/
|
||||||
|
[Bb]in/
|
||||||
|
[Oo]bj/
|
||||||
|
[Ll]og/
|
||||||
|
[Ll]ogs/
|
||||||
|
target/*
|
||||||
|
|
||||||
|
# Platform-specific settings
|
||||||
|
.DS_Store
|
||||||
|
*.crc_block
|
||||||
|
*.no_crc
|
||||||
|
|
||||||
|
# VisualStudioCode
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/settings.json
|
||||||
|
!.vscode/tasks.json
|
||||||
|
!.vscode/launch.json
|
||||||
|
!.vscode/extensions.json
|
||||||
|
*.code-workspace
|
||||||
|
|
||||||
|
# Ignore all local history of files
|
||||||
|
**/.history
|
||||||
|
|
||||||
|
# Windows
|
||||||
|
Thumbs.db
|
||||||
|
*.stackdump
|
||||||
|
[Dd]esktop.ini
|
||||||
|
|
||||||
|
# C Prerequisites
|
||||||
|
*.d
|
||||||
|
|
||||||
|
# C Object files
|
||||||
|
*.o
|
||||||
|
*.ko
|
||||||
|
*.obj
|
||||||
|
*.elf
|
||||||
|
|
||||||
|
# C Linker output
|
||||||
|
*.map
|
||||||
|
|
||||||
|
# C Debug files
|
||||||
|
*.dSYM/
|
||||||
|
*.su
|
||||||
|
*.idb
|
||||||
|
*.pdb
|
||||||
100
Particle/SecurityMonitor/README.md
Normal file
100
Particle/SecurityMonitor/README.md
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
# SecurityMonitor
|
||||||
|
|
||||||
|
This firmware project was created using [Particle Developer Tools](https://www.particle.io/developer-tools/) and is compatible with all [Particle Devices](https://www.particle.io/devices/).
|
||||||
|
|
||||||
|
Feel free to replace this README.md file with your own content, or keep it for reference.
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
- [Introduction](#introduction)
|
||||||
|
- [Prerequisites To Use This Template](#prerequisites-to-use-this-repository)
|
||||||
|
- [Getting Started](#getting-started)
|
||||||
|
- [Particle Firmware At A Glance](#particle-firmware-at-a-glance)
|
||||||
|
- [Logging](#logging)
|
||||||
|
- [Setup and Loop](#setup-and-loop)
|
||||||
|
- [Delays and Timing](#delays-and-timing)
|
||||||
|
- [Testing and Debugging](#testing-and-debugging)
|
||||||
|
- [GitHub Actions (CI/CD)](#github-actions-cicd)
|
||||||
|
- [OTA](#ota)
|
||||||
|
- [Support and Feedback](#support-and-feedback)
|
||||||
|
- [Version](#version)
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
For an in-depth understanding of this project template, please refer to our [documentation](https://docs.particle.io/firmware/best-practices/firmware-template/).
|
||||||
|
|
||||||
|
## Prerequisites To Use This Repository
|
||||||
|
|
||||||
|
To use this software/firmware on a device, you'll need:
|
||||||
|
|
||||||
|
- A [Particle Device](https://www.particle.io/devices/).
|
||||||
|
- Windows/Mac/Linux for building the software and flashing it to a device.
|
||||||
|
- [Particle Development Tools](https://docs.particle.io/getting-started/developer-tools/developer-tools/) installed and set up on your computer.
|
||||||
|
- Optionally, a nice cup of tea (and perhaps a biscuit).
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
1. While not essential, we recommend running the [device setup process](https://setup.particle.io/) on your Particle device first. This ensures your device's firmware is up-to-date and you have a solid baseline to start from.
|
||||||
|
|
||||||
|
2. If you haven't already, open this project in Visual Studio Code (File -> Open Folder). Then [compile and flash](https://docs.particle.io/getting-started/developer-tools/workbench/#cloud-build-and-flash) your device. Ensure your device's USB port is connected to your computer.
|
||||||
|
|
||||||
|
3. Verify the device's operation by monitoring its logging output:
|
||||||
|
- In Visual Studio Code with the Particle Plugin, open the [command palette](https://docs.particle.io/getting-started/developer-tools/workbench/#particle-commands) and choose "Particle: Serial Monitor".
|
||||||
|
- Or, using the Particle CLI, execute:
|
||||||
|
```
|
||||||
|
particle serial monitor --follow
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Uncomment the code at the bottom of the cpp file in your src directory to publish to the Particle Cloud! Login to console.particle.io to view your devices events in real time.
|
||||||
|
|
||||||
|
5. Customize this project! For firmware details, see [Particle firmware](https://docs.particle.io/reference/device-os/api/introduction/getting-started/). For information on the project's directory structure, visit [this link](https://docs.particle.io/firmware/best-practices/firmware-template/#project-overview).
|
||||||
|
|
||||||
|
## Particle Firmware At A Glance
|
||||||
|
|
||||||
|
### Logging
|
||||||
|
|
||||||
|
The firmware includes a [logging library](https://docs.particle.io/reference/device-os/api/logging/logger-class/). You can display messages at different levels and filter them:
|
||||||
|
|
||||||
|
```
|
||||||
|
Log.trace("This is trace message");
|
||||||
|
Log.info("This is info message");
|
||||||
|
Log.warn("This is warn message");
|
||||||
|
Log.error("This is error message");
|
||||||
|
```
|
||||||
|
|
||||||
|
### Setup and Loop
|
||||||
|
|
||||||
|
Particle projects originate from the Wiring/Processing framework, which is based on C++. Typically, one-time setup functions are placed in `setup()`, and the main application runs from the `loop()` function.
|
||||||
|
|
||||||
|
For advanced scenarios, explore our [threading support](https://docs.particle.io/firmware/software-design/threading-explainer/).
|
||||||
|
|
||||||
|
### Delays and Timing
|
||||||
|
|
||||||
|
By default, the setup() and loop() functions are blocking whilst they run, meaning that if you put in a delay, your entire application will wait for that delay to finish before anything else can run.
|
||||||
|
|
||||||
|
For techniques that allow you to run multiple tasks in parallel without creating threads, checkout the code example [here](https://docs.particle.io/firmware/best-practices/firmware-template/).
|
||||||
|
|
||||||
|
(Note: Although using `delay()` isn't recommended for best practices, it's acceptable for testing.)
|
||||||
|
|
||||||
|
### Testing and Debugging
|
||||||
|
|
||||||
|
For firmware testing and debugging guidance, check [this documentation](https://docs.particle.io/troubleshooting/guides/build-tools-troubleshooting/debugging-firmware-builds/).
|
||||||
|
|
||||||
|
### GitHub Actions (CI/CD)
|
||||||
|
|
||||||
|
This project provides a YAML file for GitHub, automating firmware compilation whenever changes are pushed. More details on [Particle GitHub Actions](https://docs.particle.io/firmware/best-practices/github-actions/) are available.
|
||||||
|
|
||||||
|
### OTA
|
||||||
|
|
||||||
|
To learn how to utilize Particle's OTA service for device updates, consult [this documentation](https://docs.particle.io/getting-started/cloud/ota-updates/).
|
||||||
|
|
||||||
|
Test OTA with the 'Particle: Cloud Flash' command in Visual Studio Code or the CLI command 'particle flash'!
|
||||||
|
|
||||||
|
This firmware supports binary assets in OTA packages, allowing the inclusion of audio, images, configurations, and external microcontroller firmware. More details are [here](https://docs.particle.io/reference/device-os/api/asset-ota/asset-ota/).
|
||||||
|
|
||||||
|
## Support and Feedback
|
||||||
|
|
||||||
|
For support or feedback on this template or any Particle products, please join our [community](https://community.particle.io)!
|
||||||
|
|
||||||
|
## Version
|
||||||
|
|
||||||
|
Template version 1.0.2
|
||||||
2
Particle/SecurityMonitor/project.properties
Normal file
2
Particle/SecurityMonitor/project.properties
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
name=SecurityMonitor
|
||||||
|
#assetOtaDir=assets
|
||||||
708
Particle/SecurityMonitor/src/SecurityMonitor.cpp
Normal file
708
Particle/SecurityMonitor/src/SecurityMonitor.cpp
Normal file
@ -0,0 +1,708 @@
|
|||||||
|
#include "Particle.h"
|
||||||
|
|
||||||
|
// Ultra low power system settings for Boron LTE
|
||||||
|
SYSTEM_MODE(MANUAL); // Manual control over cellular connection
|
||||||
|
SYSTEM_THREAD(ENABLED);
|
||||||
|
|
||||||
|
// DEBUG MODE - Enable for troubleshooting (DISABLE in production!)
|
||||||
|
// #define DEBUG_MODE
|
||||||
|
#ifdef DEBUG_MODE
|
||||||
|
#define DEBUG_SERIAL_SPEED 9600
|
||||||
|
#define DEBUG_DELAY 100
|
||||||
|
SerialLogHandler logHandler(LOG_LEVEL_ALL);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Pin definitions for Boron
|
||||||
|
const int MICROSWITCH_PIN = D2; // Microswitch (NC - normally closed)
|
||||||
|
const int ALARM_PIN = D6; // Alarm output
|
||||||
|
const int STATUS_LED = D7; // Built-in LED for status
|
||||||
|
|
||||||
|
// Ultra low power settings for LTE Boron
|
||||||
|
const unsigned long DAILY_BATTERY_REPORT = 86400; // 24 hours in seconds
|
||||||
|
const unsigned long ALARM_DURATION = 10000; // 10 seconds alarm duration
|
||||||
|
const unsigned long CELLULAR_TIMEOUT = 120000; // 120 seconds to connect to cellular
|
||||||
|
const unsigned long PUBLISH_TIMEOUT = 30000; // 30 seconds to publish
|
||||||
|
|
||||||
|
// Persistent state (survives STOP mode sleep)
|
||||||
|
retained unsigned long lastBatteryReport = 0;
|
||||||
|
retained unsigned long bootCount = 0;
|
||||||
|
retained bool deviceInitialized = false;
|
||||||
|
retained float lastReportedBattery = 100.0;
|
||||||
|
|
||||||
|
// Wake up reasons for Boron
|
||||||
|
enum BoronWakeReason {
|
||||||
|
WAKE_SECURITY_BREACH, // Microswitch triggered
|
||||||
|
WAKE_DAILY_REPORT, // Daily battery check
|
||||||
|
WAKE_COLD_START // Power on/reset
|
||||||
|
};
|
||||||
|
|
||||||
|
// Function prototypes
|
||||||
|
BoronWakeReason determineWakeReason();
|
||||||
|
void handleSecurityBreach();
|
||||||
|
void handleDailyReport();
|
||||||
|
void handleColdStart();
|
||||||
|
void activateSecurityAlarm();
|
||||||
|
void maintainAlarmDuration();
|
||||||
|
bool connectToCellular();
|
||||||
|
void publishSecurityAlert(String alertData);
|
||||||
|
String createSecurityAlert();
|
||||||
|
String createBatteryReport(float batteryLevel);
|
||||||
|
String createStartupAlert();
|
||||||
|
void flashStatusLED(int count, int duration);
|
||||||
|
void enterUltraLowPowerSleep();
|
||||||
|
void handleWakeUp(SystemSleepResult sleepResult);
|
||||||
|
|
||||||
|
// Debug helper functions
|
||||||
|
#ifdef DEBUG_MODE
|
||||||
|
void debugPrint(String message) {
|
||||||
|
Serial.printf("[%lu] %s\r\n", millis(), message.c_str());
|
||||||
|
Serial.flush();
|
||||||
|
delay(DEBUG_DELAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
void debugSystemState() {
|
||||||
|
debugPrint("=== SYSTEM STATE ===");
|
||||||
|
debugPrint("Boot count: " + String(bootCount));
|
||||||
|
debugPrint("Device initialized: " + String(deviceInitialized ? "YES" : "NO"));
|
||||||
|
debugPrint("Battery: " + String(System.batteryCharge()) + "%");
|
||||||
|
debugPrint("Free memory: " + String(System.freeMemory()) + " bytes");
|
||||||
|
debugPrint("Uptime: " + String(millis() / 1000) + " seconds");
|
||||||
|
debugPrint("D2 (switch): " + String(digitalRead(MICROSWITCH_PIN) ? "HIGH" : "LOW"));
|
||||||
|
debugPrint("D3 (alarm): " + String(digitalRead(ALARM_PIN) ? "HIGH" : "LOW"));
|
||||||
|
debugPrint("Last battery report: " + String(lastBatteryReport));
|
||||||
|
debugPrint("Time now: " + String(Time.now()));
|
||||||
|
if (Cellular.ready()) {
|
||||||
|
debugPrint("Cellular signal: " + String(Cellular.RSSI().getStrength()) + " dBm");
|
||||||
|
debugPrint("Cellular quality: " + String(Cellular.RSSI().getQuality()));
|
||||||
|
} else {
|
||||||
|
debugPrint("Cellular: NOT READY");
|
||||||
|
}
|
||||||
|
debugPrint("==================");
|
||||||
|
}
|
||||||
|
|
||||||
|
void debugPinStates() {
|
||||||
|
debugPrint("=== PIN STATES ===");
|
||||||
|
debugPrint("D2 (MICROSWITCH): " + String(digitalRead(MICROSWITCH_PIN)));
|
||||||
|
debugPrint("D3 (ALARM): " + String(digitalRead(ALARM_PIN)));
|
||||||
|
debugPrint("D7 (STATUS_LED): " + String(digitalRead(STATUS_LED)));
|
||||||
|
debugPrint("==================");
|
||||||
|
}
|
||||||
|
|
||||||
|
void debugSleepResult(SystemSleepResult result) {
|
||||||
|
debugPrint("=== SLEEP WAKE DETAILS ===");
|
||||||
|
debugPrint("Wake reason: " + String((int)result.wakeupReason()));
|
||||||
|
debugPrint("Wake pin: " + String(result.wakeupPin()));
|
||||||
|
// debugPrint("RTC wake: " + String(result.rtcWakeup() ? "YES" : "NO"));
|
||||||
|
debugPrint("Error: " + String((int)result.error()));
|
||||||
|
debugPrint("========================");
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#define debugPrint(message)
|
||||||
|
#define debugSystemState()
|
||||||
|
#define debugPinStates()
|
||||||
|
#define debugSleepResult(result)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
bootCount++;
|
||||||
|
|
||||||
|
#ifdef DEBUG_MODE
|
||||||
|
Serial.begin(DEBUG_SERIAL_SPEED);
|
||||||
|
delay(3000); // Wait for serial connection
|
||||||
|
debugPrint("=== BORON SECURITY DEVICE STARTUP ===");
|
||||||
|
debugPrint("Firmware version: 1.0.0");
|
||||||
|
debugPrint("Build date: " + String(__DATE__) + " " + String(__TIME__));
|
||||||
|
debugPrint("Device ID: " + System.deviceID());
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Initialize pins immediately for security
|
||||||
|
pinMode(MICROSWITCH_PIN, INPUT_PULLUP); // Pullup for NC switch
|
||||||
|
pinMode(ALARM_PIN, OUTPUT);
|
||||||
|
pinMode(STATUS_LED, OUTPUT);
|
||||||
|
|
||||||
|
// Ensure alarm starts OFF
|
||||||
|
digitalWrite(ALARM_PIN, LOW);
|
||||||
|
digitalWrite(STATUS_LED, LOW);
|
||||||
|
|
||||||
|
debugPrint("Pin initialization complete");
|
||||||
|
debugPinStates();
|
||||||
|
debugSystemState();
|
||||||
|
|
||||||
|
// Add delay to stabilize pin readings
|
||||||
|
delay(500);
|
||||||
|
|
||||||
|
// Determine why we woke up
|
||||||
|
debugPrint("Determining wake reason...");
|
||||||
|
BoronWakeReason wakeReason = determineWakeReason();
|
||||||
|
|
||||||
|
switch (wakeReason) {
|
||||||
|
case WAKE_SECURITY_BREACH:
|
||||||
|
debugPrint("Wake reason: SECURITY BREACH");
|
||||||
|
break;
|
||||||
|
case WAKE_DAILY_REPORT:
|
||||||
|
debugPrint("Wake reason: DAILY REPORT");
|
||||||
|
break;
|
||||||
|
case WAKE_COLD_START:
|
||||||
|
debugPrint("Wake reason: COLD START");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle based on wake reason
|
||||||
|
switch (wakeReason) {
|
||||||
|
case WAKE_SECURITY_BREACH:
|
||||||
|
handleSecurityBreach();
|
||||||
|
break;
|
||||||
|
case WAKE_DAILY_REPORT:
|
||||||
|
handleDailyReport();
|
||||||
|
break;
|
||||||
|
case WAKE_COLD_START:
|
||||||
|
handleColdStart();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
debugPrint("Main processing complete, preparing for sleep");
|
||||||
|
debugSystemState();
|
||||||
|
debugPinStates();
|
||||||
|
|
||||||
|
// Add delay before sleep to ensure all debug output is sent
|
||||||
|
debugPrint("Waiting before sleep to ensure debug output complete...");
|
||||||
|
delay(2000);
|
||||||
|
|
||||||
|
// Enter ultra low power sleep and handle wake-up in a loop
|
||||||
|
enterUltraLowPowerSleep();
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
// Should never reach here in ultra low power mode
|
||||||
|
debugPrint("ERROR: Reached loop() - this should not happen!");
|
||||||
|
debugPrint("Going to sleep immediately...");
|
||||||
|
delay(1000);
|
||||||
|
enterUltraLowPowerSleep();
|
||||||
|
}
|
||||||
|
|
||||||
|
BoronWakeReason determineWakeReason() {
|
||||||
|
debugPrint("Checking wake reason...");
|
||||||
|
|
||||||
|
// Check if microswitch is open (security breach)
|
||||||
|
// NC switch: HIGH = closed (normal), LOW = open (breach)
|
||||||
|
bool switchOpen = (digitalRead(MICROSWITCH_PIN) == LOW);
|
||||||
|
debugPrint("Microswitch raw reading: " + String(digitalRead(MICROSWITCH_PIN)));
|
||||||
|
debugPrint("Microswitch state: " + String(switchOpen ? "OPEN (BREACH)" : "CLOSED (NORMAL)"));
|
||||||
|
|
||||||
|
// Double-check pin reading with delay
|
||||||
|
delay(100);
|
||||||
|
bool switchOpen2 = (digitalRead(MICROSWITCH_PIN) == LOW);
|
||||||
|
debugPrint("Microswitch second reading: " + String(digitalRead(MICROSWITCH_PIN)));
|
||||||
|
debugPrint("Microswitch confirmed: " + String(switchOpen2 ? "OPEN (BREACH)" : "CLOSED (NORMAL)"));
|
||||||
|
|
||||||
|
if (switchOpen && switchOpen2) {
|
||||||
|
debugPrint("Security breach detected!");
|
||||||
|
return WAKE_SECURITY_BREACH;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if it's time for daily battery report
|
||||||
|
unsigned long currentTime = Time.now();
|
||||||
|
debugPrint("Current time: " + String(currentTime));
|
||||||
|
debugPrint("Last battery report: " + String(lastBatteryReport));
|
||||||
|
debugPrint("Time since last report: " + String(currentTime - lastBatteryReport) + " seconds");
|
||||||
|
debugPrint("Report interval: " + String(DAILY_BATTERY_REPORT) + " seconds");
|
||||||
|
|
||||||
|
if (deviceInitialized && (currentTime - lastBatteryReport) >= DAILY_BATTERY_REPORT) {
|
||||||
|
debugPrint("Time for daily battery report");
|
||||||
|
return WAKE_DAILY_REPORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must be a cold start (power on, reset, or first boot)
|
||||||
|
debugPrint("Determined as cold start");
|
||||||
|
return WAKE_COLD_START;
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleSecurityBreach() {
|
||||||
|
debugPrint("=== HANDLING SECURITY BREACH ===");
|
||||||
|
|
||||||
|
// IMMEDIATE ALARM ACTIVATION FIRST - before any other processing
|
||||||
|
debugPrint("IMMEDIATE: Activating security alarm...");
|
||||||
|
activateSecurityAlarm();
|
||||||
|
|
||||||
|
// Status indication
|
||||||
|
debugPrint("Flashing status LED...");
|
||||||
|
flashStatusLED(5, 100); // 5 rapid flashes
|
||||||
|
|
||||||
|
// Now connect and send alert
|
||||||
|
debugPrint("Attempting cellular connection...");
|
||||||
|
if (connectToCellular()) {
|
||||||
|
debugPrint("Cellular connected, creating alert...");
|
||||||
|
String alertData = createSecurityAlert();
|
||||||
|
debugPrint("Alert data: " + alertData);
|
||||||
|
publishSecurityAlert(alertData);
|
||||||
|
|
||||||
|
// Brief delay to ensure message is sent
|
||||||
|
debugPrint("Waiting for message transmission...");
|
||||||
|
delay(5000);
|
||||||
|
} else {
|
||||||
|
debugPrint("Failed to connect to cellular!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep alarm on for full duration even if network fails
|
||||||
|
debugPrint("Maintaining alarm duration...");
|
||||||
|
maintainAlarmDuration();
|
||||||
|
|
||||||
|
// Turn off alarm
|
||||||
|
debugPrint("Deactivating alarm");
|
||||||
|
digitalWrite(ALARM_PIN, LOW);
|
||||||
|
digitalWrite(STATUS_LED, LOW);
|
||||||
|
debugPrint("Security breach handling complete");
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleDailyReport() {
|
||||||
|
debugPrint("=== HANDLING DAILY REPORT ===");
|
||||||
|
|
||||||
|
// Quick status blink
|
||||||
|
flashStatusLED(2, 200);
|
||||||
|
|
||||||
|
// Connect and send daily battery report
|
||||||
|
debugPrint("Attempting cellular connection for daily report...");
|
||||||
|
if (connectToCellular()) {
|
||||||
|
float currentBattery = System.batteryCharge();
|
||||||
|
debugPrint("Current battery level: " + String(currentBattery) + "%");
|
||||||
|
String reportData = createBatteryReport(currentBattery);
|
||||||
|
debugPrint("Report data: " + reportData);
|
||||||
|
publishSecurityAlert(reportData);
|
||||||
|
|
||||||
|
// Update tracking
|
||||||
|
lastBatteryReport = Time.now();
|
||||||
|
lastReportedBattery = currentBattery;
|
||||||
|
debugPrint("Updated last battery report time: " + String(lastBatteryReport));
|
||||||
|
|
||||||
|
delay(3000); // Ensure transmission completes
|
||||||
|
} else {
|
||||||
|
debugPrint("Failed to connect to cellular for daily report!");
|
||||||
|
}
|
||||||
|
debugPrint("Daily report handling complete");
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleColdStart() {
|
||||||
|
debugPrint("=== HANDLING COLD START ===");
|
||||||
|
|
||||||
|
// Device just powered on or reset
|
||||||
|
deviceInitialized = false;
|
||||||
|
|
||||||
|
// Startup indication (3 slow blinks)
|
||||||
|
debugPrint("Startup indication...");
|
||||||
|
flashStatusLED(3, 500);
|
||||||
|
|
||||||
|
debugPrint("Attempting cellular connection for startup alert...");
|
||||||
|
if (connectToCellular()) {
|
||||||
|
String startupData = createStartupAlert();
|
||||||
|
debugPrint("Startup data: " + startupData);
|
||||||
|
publishSecurityAlert(startupData);
|
||||||
|
|
||||||
|
// Initialize tracking
|
||||||
|
deviceInitialized = true;
|
||||||
|
lastBatteryReport = Time.now();
|
||||||
|
lastReportedBattery = System.batteryCharge();
|
||||||
|
debugPrint("Device initialization complete");
|
||||||
|
debugPrint("Initialized battery report time: " + String(lastBatteryReport));
|
||||||
|
|
||||||
|
delay(3000);
|
||||||
|
} else {
|
||||||
|
debugPrint("Failed to connect to cellular for startup alert!");
|
||||||
|
// Still mark as initialized even if we can't report
|
||||||
|
deviceInitialized = true;
|
||||||
|
lastBatteryReport = Time.now();
|
||||||
|
lastReportedBattery = System.batteryCharge();
|
||||||
|
}
|
||||||
|
debugPrint("Cold start handling complete");
|
||||||
|
}
|
||||||
|
|
||||||
|
void activateSecurityAlarm() {
|
||||||
|
debugPrint("ALARM: IMMEDIATE activation - HIGHEST PRIORITY");
|
||||||
|
|
||||||
|
// IMMEDIATE alarm activation - highest priority
|
||||||
|
digitalWrite(ALARM_PIN, HIGH);
|
||||||
|
digitalWrite(STATUS_LED, HIGH);
|
||||||
|
|
||||||
|
debugPrint("ALARM: Alarm pin set HIGH");
|
||||||
|
debugPrint("ALARM: Current pin state - D3: " + String(digitalRead(ALARM_PIN)));
|
||||||
|
|
||||||
|
// Quick beep pattern to confirm activation
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
debugPrint("ALARM: Beep pattern " + String(i + 1));
|
||||||
|
delay(100);
|
||||||
|
digitalWrite(STATUS_LED, LOW);
|
||||||
|
delay(100);
|
||||||
|
digitalWrite(STATUS_LED, HIGH);
|
||||||
|
}
|
||||||
|
debugPrint("ALARM: Activation complete - alarm should be ON");
|
||||||
|
}
|
||||||
|
|
||||||
|
void maintainAlarmDuration() {
|
||||||
|
debugPrint("ALARM: Maintaining alarm for " + String(ALARM_DURATION) + "ms");
|
||||||
|
debugPrint("ALARM: Current alarm pin state: " + String(digitalRead(ALARM_PIN)));
|
||||||
|
|
||||||
|
// Ensure alarm is still on
|
||||||
|
digitalWrite(ALARM_PIN, HIGH);
|
||||||
|
|
||||||
|
// Keep alarm on for specified duration
|
||||||
|
unsigned long alarmStart = millis();
|
||||||
|
|
||||||
|
while (millis() - alarmStart < ALARM_DURATION) {
|
||||||
|
// Ensure alarm stays on
|
||||||
|
digitalWrite(ALARM_PIN, HIGH);
|
||||||
|
|
||||||
|
// Flash status LED while alarm is active
|
||||||
|
digitalWrite(STATUS_LED, HIGH);
|
||||||
|
delay(250);
|
||||||
|
digitalWrite(STATUS_LED, LOW);
|
||||||
|
delay(250);
|
||||||
|
|
||||||
|
// Check if switch closed again (breach ended)
|
||||||
|
if (digitalRead(MICROSWITCH_PIN) == HIGH) {
|
||||||
|
debugPrint("ALARM: Switch closed again, breach ended");
|
||||||
|
// Optionally end alarm early
|
||||||
|
// break;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long remaining = ALARM_DURATION - (millis() - alarmStart);
|
||||||
|
if (remaining % 2000 == 0) { // Log every 2 seconds
|
||||||
|
debugPrint("ALARM: Still active, " + String(remaining / 1000) + "s remaining");
|
||||||
|
debugPrint("ALARM: Pin state check - D3: " + String(digitalRead(ALARM_PIN)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
debugPrint("ALARM: Duration complete");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool connectToCellular() {
|
||||||
|
debugPrint("CELLULAR: Starting connection attempt");
|
||||||
|
unsigned long startTime = millis();
|
||||||
|
|
||||||
|
// // Status indication - connecting
|
||||||
|
// digitalWrite(STATUS_LED, HIGH);
|
||||||
|
|
||||||
|
// // Ensure cellular is off first, then turn on (fresh start)
|
||||||
|
// debugPrint("CELLULAR: Resetting cellular radio");
|
||||||
|
// Cellular.off();
|
||||||
|
// delay(2000);
|
||||||
|
|
||||||
|
// // Enable cellular radio
|
||||||
|
// debugPrint("CELLULAR: Turning on cellular radio");
|
||||||
|
// Cellular.on();
|
||||||
|
|
||||||
|
// // Wait longer for cellular ready
|
||||||
|
// debugPrint("CELLULAR: Waiting for cellular ready...");
|
||||||
|
// unsigned long cellularStart = millis();
|
||||||
|
// while (!Cellular.ready() && (millis() - cellularStart) < (CELLULAR_TIMEOUT / 2)) {
|
||||||
|
// debugPrint("CELLULAR: Still waiting for radio... " + String((millis() - cellularStart) / 1000) + "s");
|
||||||
|
// delay(5000); // Check every 5 seconds instead of 2
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (Cellular.ready()) {
|
||||||
|
// debugPrint("CELLULAR: Radio ready");
|
||||||
|
// CellularSignal signal = Cellular.RSSI();
|
||||||
|
// debugPrint("CELLULAR: Signal strength: " + String(signal.getStrength()) + " dBm");
|
||||||
|
// debugPrint("CELLULAR: Signal quality: " + String(signal.getQuality()));
|
||||||
|
|
||||||
|
// // Check signal strength
|
||||||
|
// if (signal.getStrength() < -100) {
|
||||||
|
// debugPrint("CELLULAR: WARNING - Very weak signal!");
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// debugPrint("CELLULAR: Radio failed to become ready");
|
||||||
|
// digitalWrite(STATUS_LED, LOW);
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Connect to Particle cloud via cellular
|
||||||
|
debugPrint("CELLULAR: Connecting to Particle cloud");
|
||||||
|
Particle.connect();
|
||||||
|
|
||||||
|
// Wait for connection with timeout
|
||||||
|
while (!Particle.connected() && (millis() - startTime) < CELLULAR_TIMEOUT) {
|
||||||
|
Particle.process();
|
||||||
|
|
||||||
|
// Blink during connection attempt
|
||||||
|
digitalWrite(STATUS_LED, (millis() / 500) % 2);
|
||||||
|
|
||||||
|
unsigned long elapsed = millis() - startTime;
|
||||||
|
if (elapsed % 10000 == 0) { // Log every 10 seconds
|
||||||
|
debugPrint("CELLULAR: Still connecting... " + String(elapsed / 1000) + "s elapsed");
|
||||||
|
}
|
||||||
|
|
||||||
|
delay(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool connected = Particle.connected();
|
||||||
|
|
||||||
|
if (connected) {
|
||||||
|
debugPrint("CELLULAR: Successfully connected to Particle cloud");
|
||||||
|
debugPrint("CELLULAR: Connection time: " + String((millis() - startTime) / 1000) + " seconds");
|
||||||
|
|
||||||
|
// Success - solid LED for 1 second
|
||||||
|
digitalWrite(STATUS_LED, HIGH);
|
||||||
|
delay(1000);
|
||||||
|
} else {
|
||||||
|
debugPrint("CELLULAR: Failed to connect to Particle cloud");
|
||||||
|
debugPrint("CELLULAR: Timeout after " + String(CELLULAR_TIMEOUT / 1000) + " seconds");
|
||||||
|
|
||||||
|
// Failed - rapid blinks
|
||||||
|
flashStatusLED(10, 50);
|
||||||
|
}
|
||||||
|
|
||||||
|
digitalWrite(STATUS_LED, LOW);
|
||||||
|
return connected;
|
||||||
|
}
|
||||||
|
|
||||||
|
void publishSecurityAlert(String alertData) {
|
||||||
|
debugPrint("PUBLISH: Attempting to publish alert");
|
||||||
|
debugPrint("PUBLISH: Data length: " + String(alertData.length()) + " characters");
|
||||||
|
|
||||||
|
if (!Particle.connected()) {
|
||||||
|
debugPrint("PUBLISH: Not connected to Particle cloud!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to publish with retries
|
||||||
|
for (int attempts = 0; attempts < 3; attempts++) {
|
||||||
|
debugPrint("PUBLISH: Attempt " + String(attempts + 1) + " of 3");
|
||||||
|
|
||||||
|
bool success = Particle.publish("Security Alert", alertData, PRIVATE);
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
debugPrint("PUBLISH: Success on attempt " + String(attempts + 1));
|
||||||
|
// Success indication
|
||||||
|
flashStatusLED(2, 100);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
debugPrint("PUBLISH: Failed on attempt " + String(attempts + 1));
|
||||||
|
// Retry indication
|
||||||
|
flashStatusLED(1, 50);
|
||||||
|
delay(2000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow time for message to be sent
|
||||||
|
debugPrint("PUBLISH: Waiting for transmission completion");
|
||||||
|
unsigned long publishStart = millis();
|
||||||
|
while (millis() - publishStart < PUBLISH_TIMEOUT) {
|
||||||
|
Particle.process();
|
||||||
|
delay(100);
|
||||||
|
|
||||||
|
if ((millis() - publishStart) % 10000 == 0) { // Log every 10 seconds
|
||||||
|
debugPrint("PUBLISH: Still waiting... " + String((millis() - publishStart) / 1000) + "s");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
debugPrint("PUBLISH: Transmission window complete");
|
||||||
|
}
|
||||||
|
|
||||||
|
String createSecurityAlert() {
|
||||||
|
debugPrint("Creating security alert data");
|
||||||
|
float batteryLevel = System.batteryCharge();
|
||||||
|
CellularSignal signal = Cellular.RSSI();
|
||||||
|
|
||||||
|
String alertData = String::format(
|
||||||
|
"Type:SECURITY_BREACH|Desc:Microswitch opened - INTRUDER DETECTED|Batt:%.1f%%|Signal:%d|Quality:%d|Boot:%lu|Alarm:ON",
|
||||||
|
batteryLevel,
|
||||||
|
signal.getStrength(),
|
||||||
|
signal.getQuality(),
|
||||||
|
bootCount
|
||||||
|
);
|
||||||
|
|
||||||
|
debugPrint("Security alert created: " + alertData);
|
||||||
|
return alertData;
|
||||||
|
}
|
||||||
|
|
||||||
|
String createBatteryReport(float batteryLevel) {
|
||||||
|
debugPrint("Creating battery report data");
|
||||||
|
CellularSignal signal = Cellular.RSSI();
|
||||||
|
|
||||||
|
String alertType;
|
||||||
|
String description;
|
||||||
|
|
||||||
|
if (batteryLevel <= 10.0 && batteryLevel > 0) {
|
||||||
|
alertType = "CRITICAL_BATTERY";
|
||||||
|
description = String::format("CRITICAL: Battery at %.1f%% - Device may shutdown soon", batteryLevel);
|
||||||
|
} else if (batteryLevel <= 20.0) {
|
||||||
|
alertType = "LOW_BATTERY";
|
||||||
|
description = String::format("LOW: Battery at %.1f%% - Consider servicing", batteryLevel);
|
||||||
|
} else {
|
||||||
|
alertType = "DAILY_BATTERY_REPORT";
|
||||||
|
description = String::format("Daily report: Battery at %.1f%%, operating normally", batteryLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
String alertData = String::format(
|
||||||
|
"Type:%s|Desc:%s|Batt:%.1f%%|Signal:%d|Quality:%d|Boot:%lu|Days:%lu",
|
||||||
|
alertType.c_str(),
|
||||||
|
description.c_str(),
|
||||||
|
batteryLevel,
|
||||||
|
signal.getStrength(),
|
||||||
|
signal.getQuality(),
|
||||||
|
bootCount,
|
||||||
|
(Time.now() - lastBatteryReport) / 86400
|
||||||
|
);
|
||||||
|
|
||||||
|
debugPrint("Battery report created: " + alertData);
|
||||||
|
return alertData;
|
||||||
|
}
|
||||||
|
|
||||||
|
String createStartupAlert() {
|
||||||
|
debugPrint("Creating startup alert data");
|
||||||
|
float batteryLevel = System.batteryCharge();
|
||||||
|
CellularSignal signal = Cellular.RSSI();
|
||||||
|
|
||||||
|
String description = String::format(
|
||||||
|
"Device startup - Boot #%lu, Battery: %.1f%%, Ready for security monitoring",
|
||||||
|
bootCount,
|
||||||
|
batteryLevel
|
||||||
|
);
|
||||||
|
|
||||||
|
String alertData = String::format(
|
||||||
|
"Type:DEVICE_STARTUP|Desc:%s|Batt:%.1f%%|Signal:%d|Quality:%d|Firmware:1.0.0",
|
||||||
|
description.c_str(),
|
||||||
|
batteryLevel,
|
||||||
|
signal.getStrength(),
|
||||||
|
signal.getQuality()
|
||||||
|
);
|
||||||
|
|
||||||
|
debugPrint("Startup alert created: " + alertData);
|
||||||
|
return alertData;
|
||||||
|
}
|
||||||
|
|
||||||
|
void flashStatusLED(int count, int duration) {
|
||||||
|
debugPrint("Flashing LED " + String(count) + " times, " + String(duration) + "ms each");
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
digitalWrite(STATUS_LED, HIGH);
|
||||||
|
delay(duration);
|
||||||
|
digitalWrite(STATUS_LED, LOW);
|
||||||
|
delay(duration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void enterUltraLowPowerSleep() {
|
||||||
|
debugPrint("=== ENTERING ULTRA LOW POWER SLEEP ===");
|
||||||
|
|
||||||
|
// Ensure alarm is OFF before sleep
|
||||||
|
digitalWrite(ALARM_PIN, LOW);
|
||||||
|
digitalWrite(STATUS_LED, LOW);
|
||||||
|
debugPrint("SLEEP: Alarm and LED turned OFF");
|
||||||
|
|
||||||
|
// Disconnect from cellular to save maximum power
|
||||||
|
if (Particle.connected()) {
|
||||||
|
debugPrint("SLEEP: Disconnecting from Particle cloud");
|
||||||
|
Particle.disconnect();
|
||||||
|
delay(3000); // Give more time to disconnect cleanly
|
||||||
|
}
|
||||||
|
|
||||||
|
// Turn off cellular radio completely
|
||||||
|
debugPrint("SLEEP: Turning off cellular radio");
|
||||||
|
Cellular.off();
|
||||||
|
delay(2000);
|
||||||
|
|
||||||
|
debugPrint("SLEEP: Configuring sleep mode");
|
||||||
|
debugPrint("SLEEP: Sleep duration: 24 hours");
|
||||||
|
debugPrint("SLEEP: Wake pin: D2 (FALLING edge)");
|
||||||
|
|
||||||
|
SystemSleepConfiguration config;
|
||||||
|
config.mode(SystemSleepMode::STOP) // STOP mode for Boron (supports RTC wake)
|
||||||
|
.gpio(MICROSWITCH_PIN, FALLING) // Wake on switch opening (NC switch goes LOW)
|
||||||
|
.duration(24h); // Wake once per day for battery report
|
||||||
|
|
||||||
|
debugPrint("SLEEP: Entering sleep mode NOW");
|
||||||
|
|
||||||
|
#ifdef DEBUG_MODE
|
||||||
|
Serial.flush(); // Ensure all debug output is sent
|
||||||
|
delay(500); // Extra time for serial flush
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Enter sleep loop - handle wake-ups and go back to sleep
|
||||||
|
while (true) {
|
||||||
|
// Enter sleep - device will wake on pin interrupt OR after 24 hours
|
||||||
|
SystemSleepResult result = System.sleep(config);
|
||||||
|
|
||||||
|
// When we wake up, we're here - handle the wake-up
|
||||||
|
debugPrint("SLEEP: Woke up from sleep!");
|
||||||
|
debugSleepResult(result);
|
||||||
|
|
||||||
|
// Handle the wake-up event
|
||||||
|
handleWakeUp(result);
|
||||||
|
|
||||||
|
// Ensure we're ready for next sleep
|
||||||
|
digitalWrite(ALARM_PIN, LOW);
|
||||||
|
digitalWrite(STATUS_LED, LOW);
|
||||||
|
|
||||||
|
debugPrint("SLEEP: Going back to sleep...");
|
||||||
|
|
||||||
|
#ifdef DEBUG_MODE
|
||||||
|
Serial.flush();
|
||||||
|
delay(500);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleWakeUp(SystemSleepResult sleepResult) {
|
||||||
|
debugPrint("=== HANDLING WAKE UP ===");
|
||||||
|
|
||||||
|
// Re-initialize pins (they should maintain state, but just to be sure)
|
||||||
|
pinMode(MICROSWITCH_PIN, INPUT_PULLUP);
|
||||||
|
pinMode(ALARM_PIN, OUTPUT);
|
||||||
|
pinMode(STATUS_LED, OUTPUT);
|
||||||
|
|
||||||
|
digitalWrite(ALARM_PIN, LOW);
|
||||||
|
digitalWrite(STATUS_LED, LOW);
|
||||||
|
|
||||||
|
debugSystemState();
|
||||||
|
debugPinStates();
|
||||||
|
|
||||||
|
// Determine wake reason based on sleep result
|
||||||
|
BoronWakeReason wakeReason;
|
||||||
|
|
||||||
|
if (sleepResult.wakeupReason() == SystemSleepWakeupReason::BY_GPIO) {
|
||||||
|
debugPrint("WAKE: Woke by GPIO (microswitch)");
|
||||||
|
// Verify the microswitch is actually open
|
||||||
|
delay(100); // Debounce
|
||||||
|
if (digitalRead(MICROSWITCH_PIN) == LOW) {
|
||||||
|
wakeReason = WAKE_SECURITY_BREACH;
|
||||||
|
debugPrint("WAKE: Confirmed security breach");
|
||||||
|
} else {
|
||||||
|
debugPrint("WAKE: False alarm - switch is closed");
|
||||||
|
return; // Go back to sleep immediately
|
||||||
|
}
|
||||||
|
} else if (sleepResult.wakeupReason() == SystemSleepWakeupReason::BY_RTC) {
|
||||||
|
debugPrint("WAKE: Woke by RTC (24-hour timer)");
|
||||||
|
wakeReason = WAKE_DAILY_REPORT;
|
||||||
|
} else {
|
||||||
|
debugPrint("WAKE: Unknown wake reason, treating as daily report");
|
||||||
|
wakeReason = WAKE_DAILY_REPORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle the wake reason
|
||||||
|
switch (wakeReason) {
|
||||||
|
case WAKE_SECURITY_BREACH:
|
||||||
|
debugPrint("WAKE: Handling security breach");
|
||||||
|
handleSecurityBreach();
|
||||||
|
break;
|
||||||
|
case WAKE_DAILY_REPORT:
|
||||||
|
debugPrint("WAKE: Handling daily report");
|
||||||
|
handleDailyReport();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
debugPrint("WAKE: Unknown wake reason");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
debugPrint("WAKE: Wake-up handling complete");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optional: Handle system events for debugging
|
||||||
|
void onCellularConnect() {
|
||||||
|
debugPrint("EVENT: Cellular connected");
|
||||||
|
flashStatusLED(1, 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onCellularDisconnect() {
|
||||||
|
debugPrint("EVENT: Cellular disconnected");
|
||||||
|
flashStatusLED(2, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uncomment for debugging (will increase power consumption)
|
||||||
|
// STARTUP(cellular.on());
|
||||||
|
// SYSTEM(SYSTEM_MODE(SEMI_AUTOMATIC));
|
||||||
@ -0,0 +1,146 @@
|
|||||||
|
# Webhook Service Environment Configuration
|
||||||
|
# Copy this file to .env and fill in your actual values
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# FLASK APPLICATION SETTINGS
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
# Flask secret key for session encryption and security
|
||||||
|
# Generate with: openssl rand -hex 32
|
||||||
|
FLASK_SECRET_KEY=your-flask-secret-key-generate-with-openssl-rand-hex-32
|
||||||
|
|
||||||
|
# Flask environment (development, production)
|
||||||
|
FLASK_ENV=production
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# WEBHOOK AUTHENTICATION SECRETS
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
# Secret for HMAC-SHA256 signature verification (generic webhooks)
|
||||||
|
# Generate with: openssl rand -hex 32
|
||||||
|
WEBHOOK_SECRET=your-webhook-secret-generate-with-openssl-rand-hex-32
|
||||||
|
|
||||||
|
# Secret for Particle.io webhook Bearer token authentication
|
||||||
|
# Generate with: openssl rand -hex 32
|
||||||
|
PARTICLE_WEBHOOK_SECRET=your-particle-webhook-secret-generate-with-openssl-rand-hex-32
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# EMAIL/SMS NOTIFICATION SETTINGS
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
# Gmail account for sending notifications
|
||||||
|
# IMPORTANT: Use Gmail App Password, NOT your regular password
|
||||||
|
# Instructions: https://support.google.com/accounts/answer/185833
|
||||||
|
SMTP_EMAIL=your-email@gmail.com
|
||||||
|
|
||||||
|
# Gmail App Password (16 characters, no spaces)
|
||||||
|
# Generate at: https://myaccount.google.com/apppasswords
|
||||||
|
# Example format: abcdefghijklmnop
|
||||||
|
SMTP_PASSWORD=your-gmail-app-password-16-characters-no-spaces
|
||||||
|
|
||||||
|
# SMS gateway email address for text notifications
|
||||||
|
# Format: phone-number@carrier-gateway.com
|
||||||
|
# Examples:
|
||||||
|
# Verizon: 5551234567@vtext.com
|
||||||
|
# AT&T: 5551234567@txt.att.net
|
||||||
|
# T-Mobile: 5551234567@tmomail.net
|
||||||
|
# Sprint: 5551234567@messaging.sprintpcs.com
|
||||||
|
RECIPIENT_EMAIL=your-phone-number@carrier-gateway.com
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# OPTIONAL: MULTIPLE NOTIFICATION RECIPIENTS
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
# For multiple recipients, use comma-separated values
|
||||||
|
# RECIPIENT_EMAIL=phone1@vtext.com,phone2@txt.att.net,email@domain.com
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# OPTIONAL: ADVANCED CONFIGURATION
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
# Rate limiting configuration (requests per minute)
|
||||||
|
# RATE_LIMIT_PER_MINUTE=10
|
||||||
|
# RATE_LIMIT_BURST=20
|
||||||
|
|
||||||
|
# Request timeout in seconds
|
||||||
|
# REQUEST_TIMEOUT=30
|
||||||
|
|
||||||
|
# Maximum request payload size in bytes
|
||||||
|
# MAX_PAYLOAD_SIZE=1024
|
||||||
|
|
||||||
|
# Logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
|
||||||
|
# LOG_LEVEL=INFO
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# OPTIONAL: DATABASE CONFIGURATION (Future Enhancement)
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
# Database connection string (when database support is added)
|
||||||
|
# DATABASE_URL=postgresql://webhook_user:password@localhost:5432/webhook_service
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# OPTIONAL: REDIS CONFIGURATION (Future Enhancement)
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
# Redis URL for caching and rate limiting
|
||||||
|
# REDIS_URL=redis://localhost:6379/0
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# OPTIONAL: MONITORING CONFIGURATION
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
# Prometheus metrics endpoint (true/false)
|
||||||
|
# ENABLE_METRICS=true
|
||||||
|
|
||||||
|
# Metrics server port
|
||||||
|
# METRICS_PORT=8000
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# OPTIONAL: NOTIFICATION ENHANCEMENT
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
# Slack webhook URL for notifications
|
||||||
|
# SLACK_WEBHOOK_URL=https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK
|
||||||
|
|
||||||
|
# Discord webhook URL for notifications
|
||||||
|
# DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/YOUR_WEBHOOK_ID/YOUR_WEBHOOK_TOKEN
|
||||||
|
|
||||||
|
# Telegram bot configuration
|
||||||
|
# TELEGRAM_BOT_TOKEN=your-bot-token
|
||||||
|
# TELEGRAM_CHAT_ID=your-chat-id
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# OPTIONAL: SECURITY ENHANCEMENTS
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
# IP whitelist for additional security (comma-separated)
|
||||||
|
# ALLOWED_IPS=192.168.1.0/24,10.0.0.0/8
|
||||||
|
|
||||||
|
# Enable request source validation
|
||||||
|
# VALIDATE_REQUEST_SOURCE=true
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# PRODUCTION DEPLOYMENT NOTES
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
# SECURITY REMINDERS:
|
||||||
|
# 1. Never commit this file with real values to version control
|
||||||
|
# 2. Use strong, unique passwords for all accounts
|
||||||
|
# 3. Enable 2FA on all accounts
|
||||||
|
# 4. Regularly rotate secrets and passwords
|
||||||
|
# 5. Use environment-specific configurations
|
||||||
|
|
||||||
|
# GMAIL SETUP INSTRUCTIONS:
|
||||||
|
# 1. Enable 2-Factor Authentication on your Google account
|
||||||
|
# 2. Go to https://myaccount.google.com/apppasswords
|
||||||
|
# 3. Select "Other (custom name)" and enter "Webhook Service"
|
||||||
|
# 4. Copy the generated 16-character password (no spaces)
|
||||||
|
# 5. Use this password in SMTP_PASSWORD, NOT your regular Gmail password
|
||||||
|
|
||||||
|
# SMS GATEWAY SETUP:
|
||||||
|
# Find your carrier's email-to-SMS gateway:
|
||||||
|
# - Verizon: number@vtext.com
|
||||||
|
# - AT&T: number@txt.att.net
|
||||||
|
# - T-Mobile: number@tmomail.net
|
||||||
|
# - Sprint: number@messaging.sprintpcs.com
|
||||||
|
# - US Cellular: number@email.uscc.net
|
||||||
Reference in New Issue
Block a user