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[40],DisplayBuf2[40],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
      Index = 0;
      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

183193 102389716507771 4133659 n rhhfeodpxv
Martin Smaha

Designing hardware and software since 1975.

Contact

Replications

Did you replicate this project? Share it!

I made one

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
  • 4,306
  • 81

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
  • 10,448
  • 133

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.

Use the Force... Or your Brainwaves?
Advanced
  • 17,248
  • 72

Full instructions

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

The Minimalist Thermostat
Advanced
  • 5,400
  • 34

Full instructions

A thermostat you won't see

Car or Motorbike Tracker
Advanced
  • 3,056
  • 35

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...

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

Protip

When the Particle Cloud meets the Amazon Cloud

Add projectSign up / Login