//----------------------------------------------------------------------------
//J.R. De Villiers
//May, 2026.
//----------------------------------------------------------------------------
//-------------------------------------------------
//PROJECT FEATURES:
//-------------------------------------------------
//1. Watchdog timer
//2. Burglar alarm, with following features:
// * Fully non-blocking code using millis()
// * Momentary arm/disarm toggle input with debounce
// * One normally closed burglar zone
// * One 24hr normally open panic zone
// * Siren timeout and automatic re-arm attempts
// * Armed LED with slow and fast flash modes
// * Uses static variables inside functions where possible
//3. Webserver (To communicate to ESP32 from another device)
//4. 433MHZ RF Module control (To wirelessly control relay modules)
//-------------------------------------------------
//ESP32 NOTE:
//-------------------------------------------------
//Wokwi board seen here is ESP32 WROOM-32D (Internal Antenna) DevKitC
//ESP32 Needs 3.3v power, not 5v.
//A good board is the DevKit-c using a WROOM-32U (Uses external wifi antenna for better range)
//or a WROOM-32d (Built in antenna, but shorter range)
//https://share.temu.com/CUjThLXRIKB
//Antenna duck 2.4ghz:
//https://share.temu.com/5eWTD1aGlbB
//If powering from 12v supply, then use a buck converter like this one:
//https://share.temu.com/q6V95dBFwlB (LM2596)
//-------------------------------------------
//PINOUT
//-------------------------------------------
//Safe pins (Use first):
//4,5,16,17,18,19,21,22,23,25,26,27,32,33
//* = Input Only
//** = Use with caution
//*** = Dont use, reserved for Internal Flash
// |----------------|
// 3.3V Out |3V3 GND| Ground
// EN |EN 23| GPIO23 / SPI MOSI (Virtual SPI)
//*GPIO36/ADC1_0/SensorVP/Inp Only |VP Zone1 22| GPIO22 / IC2 SCL
//*GPIO39/ADC1_3/SensorVN/Inp Only |VN Panic TX| **GPIO01 / Serial TX
//*GPIO34/ADC1_6/VDet1/Input Only |34 Arm/Disarm RX| **GPIO03 / Serial RX
//*GPIO35/ADC1_7/VDet2/Input Only |35 21| GPIO21 / IC2 SDA
// GPIO32 / ADC1_4 / 32K_XP |32 ArmedLED GND| Ground
// GPIO33 / ADC1_5 / 32K_XN |33 Siren 19| GPIO19 / SPI MISO (Virtual SPI)
// GPIO25 / ADC1_8 / DAC_1 |25 18| GOIO18 / SPI SCK (Virtual SPI)
// GPIO26 / ADC1_9 / DAC_2 |26 5| GPIO05 / SPI CS/SS (Virtual SPI)
// GPIO27 / ADC2_7 |27 17| GPIO17
//GPIO14 / ADC2_6 / SPI SCK (HSPI) |14 16| GPIO16
//**GPIO12 / ADC2_5/SPI MISO (HSPI)|12 4| GPIO04 / ADC2_0
// Ground |GND 0| **GPIO0 / ADC2_1 / BOOT
//GPIO13 / ADC2_4 / SPI MOSI (HSPI)|13 2| **GPIO02 / ADC2_2
// ***GPIO9 / Internal Flash (D2) |D2 15| **GPIO15 / ADC2_3 / SPI CS/SS (Hardware SPI)
// ***GPI10 / Internal Flash (D3) |D3 D1| ***GPIO08 / Internal Flash (D1)
// ***GPI11 / Internal Flash (CMD) |CMD D0| ***GPIO07 / Internal Flash (D0)
//5V In (Onboard reg goes to 3.3v) |5V CLK| ***GPIO06 / Internal FLash (SCK)
// |----------------|
//-------------------------------------------------------------------------------
//FOR ACCESSING THE ESP32 WEBSERVER PAGE FROM A DEVICE
//This page will then allow us to controls various lights etc.
//-------------------------------------------------------------------------------
//OPTION 1 - ESP32 makes an access point and you connect to it by calling this in 'Setup' --> WiFi.softAP(ssid, password);
//Then connect to the SSID using the password in Network Settings on phone.
//***Disadvantage of this is that your phone will then not be able to access internet while connected to the ESP32 as
//the ESP32 will create a local internal network only.
//The ESP32's default IP address it will have when it creates an access point is 192.168.43.120
//To connect to it, open a browser on your device and go to http://192.168.43.120
//OPTION 2 (Recommended) - ESP32 connects automatically to your phone's hotspot when it becomes available (ESP32 will listen for it)
//Better option as then your phone can still be connected to internet at same time.
//NOTE:
//Using the ESPmDNS library allows us to connect to the ESP32 using something like:
//http://mycaravan.local instead of an IP Address.
//------------------------------------------------------------------------------
//FOR LIGHT CONTROL OR RELAY CONTROL
//------------------------------------------------------------------------------
//1. Connect a WL102 433Mhz transmitter module to the ESP32 (First try 3.3V Power, if not working, use 5V)
//Can power directly from the ESP32 5 / 3.3v Pins.
//https://share.temu.com/sgpUj4PcygB
//2. At every light switch etc, put a 433Mhz receiver relay module (Needs 12V Power)
//it should be compatible with EV1527 to work best with the RCSwitch library.
//https://share.temu.com/LYQuQCe5GWB
//3. Put ONE receiver at a time into learning mode, by pressing its 'learn' button, then
//send a certain unique code from the transmitter on the ESP32.
//The receiver will be listening and will learn the code.
//The receiver relay can then be activated later by re-sending this code again.
//Set the reciever to 'Toggle' mode.
//-------------------------------------------------------------------
//LIBRARIES
//-------------------------------------------------------------------
#include <WiFi.h>
#include <ESPmDNS.h> //For connecting to ESP32 over wifi using name rather then IP address.
#include <RCSwitch.h> //For controlling 433MHZ relay modules.
#include <WebServer.h>
#include <Preferences.h> //To simulate EEPROM
#include <esp_task_wdt.h> //Needed for Watch dog Timer
#include "millisDelay.h" //Used for special timers so main thread does not get blocked.
#include <Wire.h> //For IC2 comms for the OLED 1306 Display
#include <Adafruit_GFX.h> //For OLED 1306 display
#include "Adafruit_SSD1306.h" //For OLED 1306 display
//-------------------------------------------------------------------
//DECLARES
//-------------------------------------------------------------------
//------------------------------------
//WIFI DECLARES
//------------------------------------
const char* ssid = "Wokwi-GUEST"; //The ssid to connect to
const char* password = ""; //The password to use
//------------------------------------
//WEBSERVER DECLARES
//------------------------------------
WebServer server(80); //Web server on port 80
//------------------------------------
//RF MODULE CONTROL 433MHZ DECLARES
//------------------------------------
RCSwitch mySwitch = RCSwitch();
bool lightState = false; //Is light currently on/off?
const int LED_BUILTIN = 2;
const byte TRANSMIT_PIN = 5; //GPIO5
//------------------------------------
//BURGLAR ALARM DECLARES
//------------------------------------
//Normally closed burglar zone input, Zone is READY when pin is grounded (LOW)
//***NOTE: IO36 has no internal pullup, so need 10k resistor between it and 3V3.
const byte ZONE_SENSE_PIN = 36;
//24hr panic input, Panic triggers when pin is pulled LOW
//***NOTE: IO39 has no internal pullup, so need 10k resistor between it and 3V3.
const byte ZONE_PANIC = 39;
//Everytime momentarily grounded, will toggle state between Arm/Disarm
//***NOTE: IO34 has no internal pullup, so need 10k resistor between it and 3V3.
const byte ARM_DISARM_MOMENTARY_PIN = 34; //Momentary arm/disarm input
const byte ARMED_LED_PIN = 32; //Armed status LED output
const byte SIREN_PIN = 33; //Siren output
const int MAX_REARM_ATTEMPTS = 3; //Maximum auto re-arm attempts after alarm
const unsigned long WAIT_TIME_BETWEEN_REARM_ATTEMPTS = 5000; //Time ms to wait before each re-arm attempt
const unsigned long DEBOUNCE_ZONE_TRIGGER_TIME = 3000; //Burglar zone must remain open this long ms before trigger
const unsigned long DEBOUNCE_PANIC_TRIGGER_TIME = 250; //Panic button must remain active this long ms before trigger
const unsigned long SIREN_TIMEOUT = 5000; //Siren auto timeout time ms
// Slow armed LED flash timing (No alarm)
const unsigned long ARMED_LED_FLASH_SLOW_ON = 250;
const unsigned long ARMED_LED_FLASH_SLOW_OFF = 750;
// Fast alarm/strobe flash timing (Alarm triggered)
const unsigned long ARMED_LED_FLASH_FAST_ON = 250;
const unsigned long ARMED_LED_FLASH_FAST_OFF = 250;
bool systemArmed = false; //True when system is armed
bool alarmTriggered = false; //True once alarm has triggered
bool sirenActive = false; //True while siren is active
bool panicPressed = false;
//True when LED should fast flash, Remains active until manually disarmed
bool strobeMode = false;
int rearmAttempts = 0; // Counts automatic re-arm attempts
//--------------------------------------
//OLED 1306 SCREEN DECLARES
//--------------------------------------
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
//Initialise IC2 comms.
//The (-1) parameter means that your OLED display doesn’t have a RESET pin.
//If your OLED display does have a RESET pin, it should be connected to a GPIO.
//In that case, you should pass the GPIO number as a parameter.
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
//--------------------------------------
//FUNCTION DECLARES.
//--------------------------------------
//Declare some function names here to avoid compiler errors.
void Connect_To_Wifi();
void Setup_mDNS();
void Setup_WebServer();
void ServeMainControlWebPage();
void HeartBeat();
void Setup_433Mhz_Transmitter();
void Setup_Burglar_Alarm();
void Setup_Heartbeat();
void Setup_Display();
void CheckBurglarAlarm();
//-------------------------------------------------------------------
//FUNCTIONS
//-------------------------------------------------------------------
////////////////////////////////////////////////////////////
void setup()
{
Serial.begin(115200);
//Connect_To_Wifi();
//Setup_Heartbeat();
//Setup_mDNS();
//Setup_WebServer();
//Setup_433Mhz_Transmitter();
Setup_Display();
Setup_Burglar_Alarm();
}
////////////////////////////////////////////////////////////
void loop()
{
//Keep web server running
server.handleClient();
//Reconnect if Wi-Fi drops
//if (WiFi.status() != WL_CONNECTED) {
//Serial.println("WiFi lost, reconnecting...");
//Connect_To_Wifi();
//}
//HeartBeat();
//mySwitch.setRepeatTransmit(10);
//mySwitch.send(123456, 24);
CheckBurglarAlarm();
}
//////////////////////////////////////////////////////////////////////////////
//SETUP FUNCTIONS
//////////////////////////////////////////////////////////////////////////////
void Setup_Display()
{
//NOTE:
//If display does not work, you may have to change the adress here from 0x3C to
//whatever your address is, use the 'GetDisplayAddress' sub to find the address of
//your device.
Wire.begin(21,22);
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C))
{
// OLED failed
while (true);
}
//GetDisplayAddress();
// Clear the buffer.
display.clearDisplay();
DrawTextCentreX("Jacques & Tracy",0,1);
}
//////////////////////////////////////////////////////////////
//Visual LED Indicator to show ESP32 has not frozen
void Setup_Heartbeat()
{
pinMode(LED_BUILTIN, OUTPUT); //For the Heartbeat
}
/////////////////////////////////////////////////////////////
void Setup_Burglar_Alarm()
{
//Normally closed to GND, triggers when goes high.
//Using external pullup 10k from it to 3v3.
pinMode(ZONE_SENSE_PIN, INPUT);
//Normally open, triggers when connected to GND.
//Using external pullup 10k from it to 3v3.
pinMode(ZONE_PANIC, INPUT);
//Everytime momentarily connected to GND will toggle Arm/Disarm.
//Using external pullup 10k from it to 3v3.
pinMode(ARM_DISARM_MOMENTARY_PIN, INPUT);
// Configure outputs
pinMode(ARMED_LED_PIN, OUTPUT);
pinMode(SIREN_PIN, OUTPUT);
// Ensure outputs start OFF
digitalWrite(ARMED_LED_PIN, LOW);
digitalWrite(SIREN_PIN, LOW);
}
////////////////////////////////////////////////////////////
//To be able to control remote 433Mhz relay board modules
void Setup_433Mhz_Transmitter()
{
mySwitch.enableTransmit(TRANSMIT_PIN); //GPIO5 connected to DATA pin of 433MHZ transmitter
}
///////////////////////////////////////////////////////////
//To connect to the ESP32 over Wifi, instead of having to type an IP address in a browser from a device,
//the connecting device can now just type: http://mycaravan.local etc.
void Setup_mDNS()
{
if (MDNS.begin("mycaravan"))
{
Serial.println("mDNS started: http://mycaravan.local");
} else
{
Serial.println("mDNS failed");
}
}
//////////////////////////////////////////////////////////
//To be able to issue commands to the ESP32 from a connected deivce.
//Device will access via its browser a webpage served by the ESP32.
void Setup_WebServer()
{
//Define the Web route handlers for the Webserver.
//When connected device sends 'http://mycaravan.local', the main page defined in 'ServeMainControlWebPage' will be served.
server.on("/", ServeMainControlWebPage);
//Connected device sends a GET request to the ESP32 equivalent to this: http://mycaravan.local/light/on
server.on("/light/on", []()
{
digitalWrite(LED_BUILTIN, HIGH);
lightState = true;
server.send(200, "text/plain", "Light ON");
});
//Connected device sends a GET request to the ESP32 equivalent to this: http://mycaravan.local/light/off
server.on("/light/off", []()
{
digitalWrite(LED_BUILTIN, LOW);
lightState = false;
server.send(200, "text/plain", "Light OFF");
});
server.on("/light/status", []()
{
server.send(200, "text/plain", lightState ? "ON" : "OFF");
});
server.begin();
Serial.println("Web server started");
}
//////////////////////////////////////////////////////////////////////
//GENERAL FUNCTIONS
//////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////
//Non blocking heartbeat to show ESP32 is up and running and not stuck in a loop.
void HeartBeat()
{
static unsigned long lastTime = 0;
static bool ledState = false;
const unsigned long interval = 1000; // 1 second
if (millis() - lastTime >= interval) {
lastTime = millis();
ledState = !ledState;
digitalWrite(LED_BUILTIN, ledState);
}
}
//////////////////////////////////////////////////////////////////////
//WIFI & WEBSERVER FUNCTIONS
//////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////
// Connect to WiFi (with retry logic)
void Connect_To_Wifi()
{
WiFi.mode(WIFI_STA); //Station Mode
WiFi.begin(ssid, password);
Serial.print("Connecting to hotspot");
int attempts = 0;
while (WiFi.status() != WL_CONNECTED && attempts < 30) {
delay(500);
Serial.print(".");
attempts++;
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println("\nConnected!");
Serial.println(WiFi.localIP());
} else {
Serial.println("\nFailed to connect (retrying...)");
}
}
//////////////////////////////////////////////////////////
//Send the html of the main control page to the connected device.
void ServeMainControlWebPage()
{
//The buttons use javascript to send a GET request from the connected device to the ESP32
//like this:
//<button onclick="fetch('/light/on')">Light ON</button> <--- fetch is the jscript function to send the GET request.
String html = R"rawliteral(
<!DOCTYPE html>
<html>
<head>
<title>Caravan Control</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body { font-family: Arial; text-align: center; margin-top: 50px; }
button { padding: 15px 30px; font-size: 18px; margin: 10px; }
</style>
</head>
<body>
<h1>Caravan Control</h1>
<button id="lightBtn" onclick="toggleLight()">Loading...</button>
<script>
async function updateStatus() {
let res = await fetch('/light/status');
let state = await res.text();
let btn = document.getElementById('lightBtn');
if (state === "ON") {
btn.innerHTML = "Turn Light OFF";
btn.style.background = "green";
} else {
btn.innerHTML = "Turn Light ON";
btn.style.background = "red";
}
}
async function toggleLight() {
let res = await fetch('/light/status');
let state = await res.text();
if (state === "ON") {
await fetch('/light/off');
} else {
await fetch('/light/on');
}
updateStatus();
}
// auto-load state when page opens
updateStatus();
</script>
</body>
</html>
)rawliteral";
server.send(200, "text/html", html);
}
/////////////////////////////////////////////////////////////////////////////////////////////
//BURGLAR ALARM FUNCTIONS
/////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////
void CheckBurglarAlarm()
{
// Check arm/disarm button
handleArmDisarmInput();
// Monitor burglar zone
handleZoneTrigger();
// Monitor panic zone
handlePanicZone();
// Handle siren timeout
handleSirenTimeout();
// Handle automatic re-arm attempts
handleRearmLogic();
// Flash armed LED
handleArmedLed();
}
// =====================================================
// CHECK IF BURGLAR ZONE IS READY
// =====================================================
// Zone is READY when grounded (LOW)
// =====================================================
bool isZoneReady()
{
return digitalRead(ZONE_SENSE_PIN) == LOW;
}
// =====================================================
// TRIGGER THE ALARM
// =====================================================
void triggerAlarm()
{
// Prevent retriggering if already active
if(panicPressed == false)
{
if (alarmTriggered)
return;
}
// Set alarm state flags
alarmTriggered = true;
sirenActive = true;
// Enable fast flashing strobe mode
strobeMode = true;
// Turn siren ON
digitalWrite(SIREN_PIN, HIGH);
Serial.println("Alarm Trigerred!");
}
// =====================================================
// HANDLE ARM / DISARM MOMENTARY INPUT
// =====================================================
// Uses debounce logic and toggle behaviour
// =====================================================
void handleArmDisarmInput()
{
// Previous button state (for edge detection)
static bool lastButtonState = HIGH;
// Debounce timer
static unsigned long lastDebounceTime = 0;
const unsigned long debounceDelay = 50;
bool currentState = digitalRead(ARM_DISARM_MOMENTARY_PIN);
// Detect any change and reset debounce timer
if (currentState != lastButtonState)
{
lastDebounceTime = millis();
}
// Only accept stable input after debounce time
if ((millis() - lastDebounceTime) > debounceDelay)
{
// Ensures one action per press
static bool buttonHandled = false;
// BUTTON PRESSED (active LOW)
if (currentState == LOW && !buttonHandled)
{
buttonHandled = true;
// =====================================================
// PRIORITY 1: ALWAYS CLEAR ALARM STATE FIRST
// =====================================================
if (alarmTriggered || sirenActive || strobeMode)
{
systemArmed = false;
alarmTriggered = false;
sirenActive = false;
strobeMode = false;
rearmAttempts = 0;
panicPressed = false;
digitalWrite(SIREN_PIN, LOW);
Serial.println("Disarmed");
return; // IMPORTANT: stop here so no toggle happens
}
// =====================================================
// NORMAL ARM / DISARM TOGGLE
// =====================================================
if (systemArmed)
{
systemArmed = false;
}
else
{
// Only allow arming if zone is ready
if (isZoneReady())
{
systemArmed = true;
Serial.println("Armed");
rearmAttempts = 0;
}
}
}
// Reset latch when button released
if (currentState == HIGH)
{
buttonHandled = false;
}
}
lastButtonState = currentState;
}
// =====================================================
// HANDLE BURGLAR ZONE
// =====================================================
// Zone must remain OPEN for 3 seconds before
// triggering the alarm
// =====================================================
void handleZoneTrigger()
{
// Time when zone first opened
static unsigned long zoneOpenStart = 0;
// Ignore if system not armed
if (!systemArmed)
{
zoneOpenStart = 0;
return;
}
// Zone is OPEN when input goes HIGH
bool zoneOpen = (digitalRead(ZONE_SENSE_PIN) == HIGH);
// Zone opened
if (zoneOpen)
{
// Start timer once
if (zoneOpenStart == 0)
{
zoneOpenStart = millis();
}
// Has zone remained open long enough?
if ((millis() - zoneOpenStart) >= DEBOUNCE_ZONE_TRIGGER_TIME)
{
triggerAlarm();
}
}
else
{
// Zone restored
zoneOpenStart = 0;
}
}
// =====================================================
// HANDLE 24HR PANIC ZONE
// =====================================================
// Panic zone active even when disarmed
// Panic must remain LOW for minimum debounce time
// =====================================================
void handlePanicZone()
{
// Time when panic input became active
static unsigned long panicStart = 0;
// Panic active when LOW
bool panicActive = (digitalRead(ZONE_PANIC) == LOW);
// Panic active
if (panicActive)
{
// Start timer once
if (panicStart == 0)
{
panicStart = millis();
}
// Has panic remained active long enough?
if ((millis() - panicStart) >= DEBOUNCE_PANIC_TRIGGER_TIME)
{
Serial.println("Panic Activated!");
triggerAlarm();
panicPressed = true;
}
}
else
{
// Reset timer when inactive
panicStart = 0;
//panicPressed = false;
}
}
// =====================================================
// HANDLE SIREN TIMEOUT
// =====================================================
// Turns siren OFF after timeout period
// =====================================================
void handleSirenTimeout()
{
// Stores siren start time
static unsigned long sirenStartTime = 0;
// Siren currently active
if (sirenActive)
{
// Start timer once
if (sirenStartTime == 0)
{
sirenStartTime = millis();
}
// Has timeout elapsed?
if ((millis() - sirenStartTime) >= SIREN_TIMEOUT)
{
// Turn siren OFF
sirenActive = false;
digitalWrite(SIREN_PIN, LOW);
Serial.println("Siren Timeout");
//alarmTriggered = false;
// Reset timer
sirenStartTime = 0;
}
}
else
{
// Ensure timer reset when inactive
sirenStartTime = 0;
}
}
// =====================================================
// HANDLE AUTO RE-ARM LOGIC
// =====================================================
// After alarm timeout:
// 1. Wait 5 seconds
// 2. Attempt re-arm
// 3. Retry up to MAX_REARM_ATTEMPTS
// =====================================================
void handleRearmLogic()
{
// True while waiting between attempts
static bool waitingToRearm = false;
// Time wait period started
static unsigned long rearmWaitStart = 0;
// Ignore if alarm never triggered
if (!alarmTriggered)
{
waitingToRearm = false;
return;
}
// Wait until siren has stopped
if (sirenActive)
{
return;
}
// Start wait timer once
if (!waitingToRearm)
{
waitingToRearm = true;
rearmWaitStart = millis();
}
// Wait period elapsed?
if ((millis() - rearmWaitStart) >= WAIT_TIME_BETWEEN_REARM_ATTEMPTS)
{
waitingToRearm = false;
// Zone ready -> re-arm system
if (isZoneReady())
{
if(panicPressed == false)
{
systemArmed = true;
alarmTriggered = false;
Serial.println("Re-Armed");
rearmAttempts = 0;
}
}
else
{
// Zone still not ready
rearmAttempts++;
Serial.println("Trying to Re-Arm but zone not ready. Attempt:" + rearmAttempts);
// Too many attempts -> give up
if (rearmAttempts >= MAX_REARM_ATTEMPTS)
{
systemArmed = false;
Serial.println("Cannot arm, given up.");
}
}
}
}
// =====================================================
// HANDLE ARMED LED FLASHING
// =====================================================
// DISARMED:
// LED OFF
//
// ARMED:
// Slow flash
//
// ALARM TRIGGERED:
// Fast flash (strobe mode)
// =====================================================
void handleArmedLed()
{
// Current LED state
static bool ledState = false;
// Last LED toggle time
static unsigned long lastToggleTime = 0;
// Fully disarmed and no strobe mode
if (!systemArmed && !strobeMode)
{
digitalWrite(ARMED_LED_PIN, LOW);
ledState = false;
return;
}
// Flash timing values
unsigned long onTime;
unsigned long offTime;
// Fast strobe mode
if (strobeMode)
{
onTime = ARMED_LED_FLASH_FAST_ON;
offTime = ARMED_LED_FLASH_FAST_OFF;
}
else
{
// Normal armed slow flash
onTime = ARMED_LED_FLASH_SLOW_ON;
offTime = ARMED_LED_FLASH_SLOW_OFF;
}
// Select current interval
unsigned long interval = ledState ? onTime : offTime;
// Time to toggle LED?
if ((millis() - lastToggleTime) >= interval)
{
lastToggleTime = millis();
// Toggle LED state
ledState = !ledState;
digitalWrite(ARMED_LED_PIN, ledState);
}
}
/////////////////////////////////////////////////////////////////////////////////////////////
//OLED 1306 SCREEN FUNCTIONS
/////////////////////////////////////////////////////////////////////////////////////////////
//=============================================================================
//Draws a bitmap from the passed bitmap data at the given co-ordinates.
void DrawBitmap(const uint8_t* BitmapData,int X, int Y)
{
display.drawBitmap(X, Y, BitmapData, 128, 64, 1);
display.display();
}
//=============================================================================
//Draws the given text so that the right edge of the text (end of the text)
//aligns (touches) to the right edge of the screen.
void DrawTextAlignToRight(char* text, int Y, int TextSize)
{
display.setTextSize(TextSize);
display.setTextColor(WHITE);
int16_t x, y;
uint16_t w, h;
display.getTextBounds(text, 0, 0, &x, &y, &w, &h);
int16_t x_right = SCREEN_WIDTH - w;
display.setCursor(x_right, Y);
display.println(text);
display.display();
}
//=============================================================================
//Draws the given text in exactly the centre of the screen for the X & Y
//co-ordinates.
void DrawTextCentre(char* text, int TextSize)
{
display.setTextSize(TextSize);
display.setTextColor(WHITE);
int16_t x, y;
uint16_t w, h;
display.getTextBounds(text, 0, 0, &x, &y, &w, &h);
int16_t x_center = (SCREEN_WIDTH - w) / 2;
int16_t y_center = (SCREEN_HEIGHT - h) / 2;
display.setCursor(x_center, y_center);
display.println(text);
display.display();
}
//=============================================================================
//Draws the given text in exactly the centre of the screen for the X
//co-ordinate only.
void DrawTextCentreX(char* text, int Y, int TextSize)
{
display.setTextSize(TextSize);
display.setTextColor(WHITE);
int16_t x, y;
uint16_t w, h;
display.getTextBounds(text, 0, 0, &x, &y, &w, &h);
int16_t x_center = (SCREEN_WIDTH - w) / 2;
int16_t y_center = (SCREEN_HEIGHT - h) / 2;
display.setCursor(x_center, Y);
display.println(text);
display.display();
}
//=============================================================================
//Draws the given text in exactly the centre of the screen for the Y
//co-ordinate only.
void DrawTextCentreY(char* text, int X, int TextSize)
{
display.setTextSize(TextSize);
display.setTextColor(WHITE);
int16_t x, y;
uint16_t w, h;
display.getTextBounds(text, 0, 0, &x, &y, &w, &h);
int16_t x_center = (SCREEN_WIDTH - w) / 2;
int16_t y_center = (SCREEN_HEIGHT - h) / 2;
display.setCursor(X, y_center);
display.println(text);
display.display();
}
//=============================================================================
//Draws the text at the given co-ordinates.
void DrawText(char* text, int X, int Y,int TextSize)
{
display.setTextSize(TextSize);
display.setTextColor(WHITE);
display.setCursor(X,Y);
display.println(text);
display.display();
}
//=============================================================================
//Gets the address of the display
void GetDisplayAddress()
{
byte error, address;
int nDevices;
Serial.println("Scanning...");
nDevices = 0;
for(address = 1; address < 127; address++ ) {
Wire.beginTransmission(address);
error = Wire.endTransmission();
if (error == 0) {
Serial.print("I2C device found at address 0x");
if (address<16) {
Serial.print("0");
}
Serial.println(address,HEX);
nDevices++;
}
else if (error==4) {
Serial.print("Unknow error at address 0x");
if (address<16) {
Serial.print("0");
}
Serial.println(address,HEX);
}
}
if (nDevices == 0) {
Serial.println("No I2C devices found\n");
}
else {
Serial.println("done\n");
}
delay(5000);
}
Zone 1 Sensor (NC)
Closed
Open
Panic (NO)
Arm/Disarm
(Zone must be ready to Arm)
Disarmed (Off)
Armed (Slow FLash)
Alarm Trigerred (Fast FLash)
Arm/Disarm
Pullup
Panic Pullup
Zone Pullup
Siren