Things used in this project

Hardware components:
Photon new
Particle Photon
×1
Resistor 4.7k ohm
×4
Resistor 3.3k ohm
×1
Mfr 25frf52 10k sml
Resistor 10k ohm
×1
Mfr 25fbf52 2k21 sml
Resistor 2.21k ohm
×1
Fairchild semiconductor 1n4004. image
1N4007 – High Voltage, High Current Rated Diode
×1
Fairchild semiconductor pn2222abu. image
General Purpose Transistor NPN
×2
Fairchild pn2907abu.
General Purpose Transistor PNP
×1
Hand tools and fabrication machines:
09507 01
Soldering iron (generic)

Schematics

First Alert FA140 Interface Schematic
Connect the project to the panel wiring as shown.

Code

First Alert FA140C/C++
This is the only code you need for the entire project.
/*
 * Project FirstAlert FA140
 * Description: Simple web interface for First Alert FA140 alarm system
 * Author: Martin Smaha
 * Date: 3/7/2017
 */
#include <string.h>

 TCPServer server = TCPServer(8080);//web page port
 TCPClient client;
 TCPServer debugServer = TCPServer(23);//debug port
 TCPClient debugClient;
 char DisplayBuf1[34],DisplayBuf2[34],LANBuf[1026],Command[8],DebugBuf[64];
 int Index,DebugIndex;
 int LED = D7;
 bool isReady;
 bool isArmed;
 bool isAlarm;

 void WebHeader(void);
 void WebHeader2(void);
 void WebFooter2(void);
 void WebServer(void);
 void SerialRXProtocol(void);
 void SerialTXProtocol(char *SendString, int Count);
 void DebugTCPServer(void);

 /* keypad byte chart
 1 0xbc
 2 0xdc
 3 0x9e
 4 0xec
 5 0xae
 6 0xce
 7 0x8c
 8 0xf4
 9 0xb6
 0 0xfe
 # 0x94
 * 0xd6
 */
 char ArmStay[2] = {0x94,0x9e};//encoded # 3 sequence
 char ArmAway[2] = {0x94,0xdc};//encoded # 2 sequence
 char Disarm[5] = {0xbc,0xdc,0x9e,0xec,0xbc}; //encode your personal 4 digit pin here followed by 1 (0xbc)
 char ShowFault[1] = {0xd6}; //encoded * sequence
 int RXpin = D1; //monitor rx signal from panel
 int TXpin = D0; //transmit key press to panel

// setup() runs once, when the device is first turned on.
void setup() {
  isReady = false;
  isArmed = false;
  isAlarm = false;

  Index = 0;
  DebugIndex = 0;
  DisplayBuf1[0] = 0;
  DisplayBuf2[0] = 0;
  LANBuf[0] = 0;
  DebugBuf[0] = 0;
  Command[0] = Command[1] = Command[2] = Command[3] = 0;
  strcpy(DisplayBuf2,"*****Photon*****No Data Received");

  // start listening for clients
  server.begin();
  debugServer.begin();
  // First Alert serial port
  Serial1.begin(2400, SERIAL_8E1); // via UART RX pins
  // small blue LED on board
  pinMode(LED, OUTPUT);
  digitalWrite(LED, LOW);
  // simulated uart pins
  pinMode(RXpin, INPUT_PULLUP);
  pinMode(TXpin, OUTPUT);
  digitalWrite(TXpin, LOW);
  // time zone
  Time.zone(-8); //PST
  //Time.zone(-7); //PDT
}

// loop() runs over and over again, as quickly as it can execute.
void loop() {
  WebServer();
  SerialRXProtocol();
  DebugTCPServer();
}

// Web Header
void WebHeader(void) {
  client.print("HTTP/1.1 200 OK\r\n\r\n<html><head><title>First Alert</title><style>div {border: 2px solid #a1a1a1;padding: 10px 40px;width: 200px;border-radius: 25px;}button {width: 100px;height: 40px;background: blue;color: white;}button:hover {color: black;}</style>");
  client.print("<META HTTP-EQUIV='Pragma' CONTENT='no-cache'>");
  client.print("<META HTTP-EQUIV='Expires' CONTENT='-1'>");
  client.print("<meta content='text/html; charset=ISO-8859-1' http-equiv='Content-Type'>");
  client.print("<meta name='viewport' content='width=device-width, initial-scale=1'></head>");
}
void WebHeader2(void) {
  client.print("HTTP/1.1 200 OK\r\n\r\n<html><head><title>First Alert</title><style>div {border: 2px solid #a1a1a1;padding: 10px 40px;width: 200px;border-radius: 25px;}button {width: 100px;height: 40px;background: blue;color: white;}button:hover {color: black;}</style>");
  client.print("<META HTTP-EQUIV='Pragma' CONTENT='no-cache'>");
  client.print("<META HTTP-EQUIV='Expires' CONTENT='-1'>");
  client.print("<meta content='text/html; charset=ISO-8859-1' http-equiv='Content-Type'>");
  client.print("<meta http-equiv='refresh' content='5; url=location.href=status.html'>");
  client.print("<meta name='viewport' content='width=device-width, initial-scale=1'></head>");
}
void WebFooter2(void) {
  client.print("<p id='counter'>Autoupdates in 5 seconds.</p>");
  client.print("<script>");
  client.print("var countDown = 5\n");
  // Update the count down every 1 second
  client.print("var x = setInterval(function() {\n");
  client.print("countDown = countDown - 1\n");
  // Display the result in the element with id="counter"
  client.print("document.getElementById('counter').innerHTML = 'Autoupdates in ' + countDown + ' seconds.' \n");
  client.print("}, 1000)\n");
  client.print("</script></div></body></html>");
}

// Web server
void WebServer(void) {
  if (client.connected()) {
    int isRequest = -1;
    if (client.available()) { //get first data segment
      memset(LANBuf,0,sizeof(LANBuf));//clear old data
      client.read((uint8_t*)LANBuf,sizeof(LANBuf) - 2);//leave the buffer zero terminated
      //determine which page browser has requested
      if (strstr(LANBuf,"GET") != NULL) {
        isRequest = 0;
      }
      if (strstr(LANBuf,"status.html") != NULL) {
        isRequest = 1;
      }
      if (strstr(LANBuf,"away.html") != NULL) {
        isRequest = 2;
      }
      if (strstr(LANBuf,"stay.html") != NULL) {
        isRequest = 3;
      }
      if (strstr(LANBuf,"disarm.html") != NULL) {
        isRequest = 4;
      }
      if (strstr(LANBuf,"test.html") != NULL) {
        isRequest = 5;
      }
      if (strstr(LANBuf,"showfault.html") != NULL) {
        isRequest = 6;
      }
    }
    //send response
    if (isRequest == 0) { //standard http error message for page not found
      client.print("HTTP/1.1 404 \r\n\r\n");
      client.stop();
    }
    if (isRequest == 1) { //status.html
      WebHeader();
      client.print("<body bgcolor=#ffffff width=250px>");
      //ALPHA display area is 2 lines of 16 characters each
      client.print("<div style=background:#eeeeee><font face=verdana color=green><b><center>");
      client.write((uint8_t*)DisplayBuf2,16);
      client.print("<br>");
      client.write((uint8_t*)DisplayBuf2+16,16);
      client.print("</b></center></font></div><div>");
      // status LEDs
      if (isReady) { //ready led bit
        client.print("<p><span style=background:#00ff00;> &nbsp &nbsp </span><font>&nbsp READY</font>");
      } else {
        client.print("<p><span style=background:#cccccc;> &nbsp &nbsp </span><font>&nbsp READY</font>");
      }
      if (isArmed) { //armed led bit
        client.print("<p><span style=background:#ff0000;> &nbsp &nbsp </span><font>&nbsp ARMED</font>");
      } else {
        client.print("<p><span style=background:#cccccc;> &nbsp &nbsp </span><font>&nbsp ARMED</font>");
      }
      //button area
      client.print("<center><hr>");
      client.print("<p><button  onclick=location.href='away.html'>AWAY</button></p>");
      client.print("<p><button  onclick=location.href='stay.html'>STAY</button></p>");
      client.print("<p><button  onclick=location.href='disarm.html'>DISARM</button></p>");
      client.print("<p><button  onclick=location.href='test.html'>TEST</button></p>");
      client.print("<p><button  onclick=location.href='showfault.html'>*</button></p><hr><p>");
      client.print(Time.timeStr());
      client.print("</p></center></div></body></html>");
      client.stop();
    }
    if (isRequest == 2){ //away.html
      WebHeader2();
      client.print("<body><div><H1>Armed Away</H1>");
      WebFooter2();
      client.stop();
      SerialTXProtocol(ArmAway,2);
    }
    if (isRequest == 3){ //stay.html
      WebHeader2();
      client.print("<body><div><H1>Armed Stay</H1>");
      WebFooter2();
      client.stop();
      SerialTXProtocol(ArmStay,2);
    }
    if (isRequest == 4){ //disarm.html
      WebHeader2();
      client.print("<body><div><H1>Disarmed</H1>");
      WebFooter2();
      client.stop();
      SerialTXProtocol(Disarm,5);
    }
    if (isRequest == 5){ //test.html
      WebHeader2();
      client.print("<body><div><H1>Test Alarm Published</H1>");
      WebFooter2();
      client.stop();
      Particle.publish("ALARM", "This was just a test.", 60, PRIVATE);
    }
    if (isRequest == 6){ //showfault.html
      WebHeader2();
      client.print("<body><div><H1>Show Faults Requested</H1>");
      WebFooter2();
      client.stop();
      SerialTXProtocol(ShowFault,1);
    }
  } else {
    // if no client is yet connected, check for a new connection
    client = server.available();
  }
}

//SerialRXProtocol
void SerialRXProtocol(void){
  // First Alert serial data
  while (Serial1.available()) {
    uint8_t inByte = Serial1.read();
    if (DebugIndex < 64) {
      DebugBuf[DebugIndex++] = inByte;
    }
    //all packets are in 4 byte chunks but text commands have 4 bytes following
    Command[0] = Command[1];
    Command[1] = Command[2];
    Command[2] = Command[3];
    Command[3] = Command[4];
    Command[4] = Command[5];
    Command[5] = Command[6];
    Command[6] = Command[7];
    Command[7] = inByte;//insert new byte
    if (Command[0] == 0 && Command[1] == 0 && Command[2] == 0 && Command[3] == 0xfe) {//text start chunk
      memcpy(DisplayBuf1,Command+4,4);
      DisplayBuf1[0] &= 0x7F;//sometimes high order bit is set on first byte to control keypad backlight
      Index = 4;
      digitalWrite(LED, HIGH);
    }
    if (Command[0] == 0 && Command[1] == 0 && Command[2] == 0 && Command[3] == 0xff) {//text continue chunk
      memcpy(DisplayBuf1+Index,Command+4,4);
      Index += 4;
    }
    if (Index >= 32) { //when enough received, copy to web buffer
      memcpy(DisplayBuf2,DisplayBuf1,32);
      DisplayBuf2[32] = 0;
      digitalWrite(LED, LOW);
      if (strstr(DisplayBuf2,"READY") != NULL) {
        isReady = true;
      } else {
        isReady = false;
      }
      if (strstr(DisplayBuf2,"ARMED ") != NULL) {
        isArmed = true;
      } else {
        isArmed = false;
      }
      if (strstr(DisplayBuf2,"ALARM") != NULL) {
        if (isAlarm == false){//only publish on transision into alarm
          Particle.publish("ALARM", DisplayBuf2, 60, PRIVATE);
        }
        isAlarm = true;
      } else {
        isAlarm = false;
      }
    }
  }
}

//SerialTXProtocol
void SerialTXProtocol(char *SendString, int Count){
  // First Alert serial data
  for (int sent = 0; sent < Count; sent++) { //do all bytes
    // wait for RXpin to go high for 4ms (actual wire goes low when RXpin goes high)
    unsigned long start_us = 0, current_us = 0, delta_us = 0;
    int delay_ms = 200; //simulate person pressing keys so not too fast between presses
    while (delta_us < 4000) {
      if (pinReadFast(RXpin) == LOW) {//keep counter reset
        start_us = micros();
      }
      current_us = micros();
      if (current_us < start_us) current_us += 35791294;//account for value wrap at 35,791,294us
      delta_us = current_us - start_us;
    }
    for (int i = 0; i < 3; i++) { //send each byte 3 times in a row
      char mask = 0x80;
      for (int j = 0; j < 8; j++) { //send each bit (MSB first) simulating uart as start, 5 data, even parity, 1 stop
        if (SendString[sent] & mask) {
          pinSetFast(TXpin); //puts 12volts on wire to panel
        } else {
          pinResetFast(TXpin); //removes 12volts from wire to panel
        }
        delayMicroseconds(416); //bit time at 2400 baud
        if (pinReadFast(RXpin) == LOW) { //panel requests sending abort
          pinResetFast(TXpin);//set low
          sent--; //resend last character
          i = 3; //break 3x loop
          delay_ms = 80; //resend in next available time slot (keypresses are allowed once every 100ms)
          break; //break this loop
        }
        mask >>= 1;//next bit
      }
    }
    delay(delay_ms);
  }
}

// Debug TCP server
void DebugTCPServer(void) {
  if (debugClient.connected()) {
    if (DebugIndex == 64) {
      debugClient.write((uint8_t*)DebugBuf,DebugIndex);
      DebugIndex = 0;
    }
    while (debugClient.available()) debugClient.read((uint8_t*)LANBuf,128); //flush unused telnet data
  } else {
    // if no client is yet connected, check for a new connection
    debugClient = debugServer.available();
  }
}

Credits

5a7f6b8f636f53ce546f0759fc3d80b7
Martin Smaha

Designing hardware and software since 1975.

Replications

Did you replicate this project? Share it!

Love this project? Think it could be improved? Tell us what you think!

Give feedback

Comments

Similar projects you might like

Mailbox 2.0
Advanced
  • 3,127
  • 79

Work in progress

Real time notifications for when your precious packages and letters arrive.

Mailbox 2.0

Team CKTS

Networked RGB Wi-Fi Decorative Touch Lights
Advanced
  • 9,839
  • 130

Full instructions

Easy to use Wi-Fi enabled touch lights that network to give you a beautiful and unobtrusive way to stay connected with the people you love.

Car or Motorbike Tracker
Advanced
  • 2,531
  • 32

Work in progress

A GPS tracker for your car, motorcycle, bike and flying saucer, with data storage and map visualization. Not too hard, but no easy either...

Use the Force... Or your Brainwaves?
Advanced
  • 13,566
  • 65

Full instructions

Thought controlled system with personal webserver and 3 working functions: robot controller, home automation and PC mouse controller.

The Minimalist Thermostat
Advanced
  • 4,663
  • 33

Full instructions

A thermostat you won't see

Email Notifications Using Amazon Web Services
Advanced
  • 3,287
  • 20

Protip

When the Particle Cloud meets the Amazon Cloud

Add projectSign up / Login
Respect project