//Open Auto code //Arezzo release July 2018 //Released under the GPL V3. //note that the number of live reservations in the system is stored in eeprom location 2046 (i.e. the last slot) //ConfigCarFun //Config string function - pass the master RFID and door mode via function. //Car types //1 = one button, wire to relay 1; pulse open, pulse close //2 = standard two button config; relay#1= open // standard, relay#2 = close //3 = two button with double push, push twice open, push twice close - i.e. open all doors, dead-lock close. //status 1/5/18: //homebox function needs writing // This #include statement was automatically added by the Particle IDE. #include #include #include "Particle.h" #define RECEIVER SoftSer #define PROTOCOL SERIAL_8N1 const int MAX_RESERVATIONS = 1000; //blocks of 15 minute lookahead for reservations. const int MAX_RESERVATION_LENGTH = 24; //blocks of 15 minutes allowed for a single reservation - 6 hours. retained int RESERVATION_POINTER = 0; //the current position of the timer relative to the reservations retained long REFERENCE_TIME; //the reference time, in 15 minute blocks stored in RAM (updated every second, stays stored while powered) - NOT initialised on reboot. long RESERVATION_BLOCK_TIME; //the start time for the current reservation block int CarType = 2; //default car type //set baud rate for software serial const uint32_t baud = 9600; //Pin asignments int OpenRelay = D4; int CloseRelay = D5; int StatusLED = D7; //temp variable for the RFID card last read value String CardReadStr; //init GPS device on Serial1 Gps _gps = Gps(&Serial1); //Create a 1ms timer to feed in the GPS data Timer _timer = Timer(1, onSerialData); #define PSS_RX D3 // RX must be interrupt enabled (on Photon/Electron D0/A5 are not) //#define PSS_TX null //this pin isn't something we use, I couldn't find a null pin so I just used the number 1. ParticleSoftSerial SoftSer(PSS_RX, 1); //note this is a work-around so that the code works on a Particle Photon (development) and an Electron (deployment) //Reservation structure for incoming reservations struct RemoteReservation { char UserID[9]; //the RFID key to unlock the doors with a null terminator, stored as text long int Epoch; //calendar day int Duration; //the number of 15 minute slots. Max reservation is 360 minutes (can be handled upstream by odoo to block out more time in multiple grouped reservations) }; //Reservation structure for local data storage in EEPROM struct LocalReservation { char UserID[4]; }; int ReservationSize = 4; //number of bytes per reservation //variable for RFID card reading process char CARDcurrent[4]; //boolean to track the door status; actually we'll only use it as a toggle. bool DoorsOpenBool = false; void clearEEPROM(); // forward declaration String ValidGPSPosition; int BatteryVoltageRead; double BatteryVoltage; //define a master RFID card which is loaded either from the EEPROM (on boot) or via the config command. String MasterRFIDCard = ""; //example = "983553fe"; char RFIDpass[8]; //define a variable for the currently active reservation card number and a boolean to say if it's live or not bool ActiveReservation = false; //Active if the reservation is within it's time period. String ReservationRFIDCard = ""; //parse reservations class ReservationCmd { String argument; public: void extractValues(String); String UserIDStr (void) { return argument.substring(argument.indexOf("UserID=")+7, argument.indexOf("StDy=")); } int Epoch (void) { return (argument.substring(argument.indexOf("Epoch=")+6, argument.indexOf("Dur="))).toInt(); } int DurationInt (void) { return (argument.substring(argument.indexOf("Dur=")+4, argument.indexOf("f"))).toInt(); } }; void ReservationCmd::extractValues (String stringPassed){ argument = stringPassed; } //parse configuration commands class ConfigCmd { String argument; public: void extractValues(String); String MasterRFIDStr (void) { return argument.substring(argument.indexOf("MasterRFID=")+11, argument.indexOf("CarType=")); } int CarTypeInt (void) { return (argument.substring(argument.indexOf("CarType=")+8, argument.indexOf("f"))).toInt(); } //eventually add some more things here? }; void ConfigCmd::extractValues (String stringPassed){ argument = stringPassed; } void setup() { debug("Running setup",1); Serial.println("Setup running..."); //set pin modes for hardwired devices pinMode(OpenRelay, OUTPUT); pinMode(CloseRelay, OUTPUT); pinMode(StatusLED, OUTPUT); pinMode(A1,AN_INPUT); //set these outputs correctly digitalWrite(OpenRelay, LOW); digitalWrite(CloseRelay, LOW); digitalWrite(StatusLED, LOW); //define functions Particle.function("Open", OpenRelayCmd); Particle.function("Close", CloseRelayCmd); Particle.function("StatusLED", StatusLEDFlash); Particle.function("MkResvn", ReserveString); Particle.function("ConfigCar", ConfigString); Particle.function("HomeBox", HomeCoords); Particle.function("WipeResvn", WipeReservations); //define variables Particle.variable("CarPos", ValidGPSPosition); Particle.variable("BatLevel", BatteryVoltage); //set up serial debug link Serial.begin(); Serial.printlnf("ready for data"); //setup RFID reader soft serial RECEIVER.begin(baud, PROTOCOL); // but SoftSerial can ;-) //Init GPS _gps.begin(9600); _gps.sendCommand(PMTK_SET_NMEA_OUTPUT_ALLDATA); _timer.start(); //retrive the master RFID card EEPROM.get(2020, RFIDpass); MasterRFIDCard = ""; MasterRFIDCard = MasterRFIDCard + RFIDpass[0]; MasterRFIDCard = MasterRFIDCard + RFIDpass[1]; MasterRFIDCard = MasterRFIDCard + RFIDpass[2]; MasterRFIDCard = MasterRFIDCard + RFIDpass[3]; MasterRFIDCard = MasterRFIDCard + RFIDpass[4]; MasterRFIDCard = MasterRFIDCard + RFIDpass[5]; MasterRFIDCard = MasterRFIDCard + RFIDpass[6]; MasterRFIDCard = MasterRFIDCard + RFIDpass[7]; Serial.print("MasterRFID set:"); Serial.print(MasterRFIDCard); debug(MasterRFIDCard,1); //set the car type from eeprom EEPROM.get(2038, CarType); debug(CarType,1); //recall the reservation pointer on power up/reset EEPROM.get(2010, RESERVATION_BLOCK_TIME); //run the 'justify' reservation eeprom routine if (REFERENCE_TIME < (time.now()/(15*60))) { //the system stopped for more than 15 minutes //we must move the reservation pointer forward to compensate debug("Clock_stopped for 15+m",1); long Ref_Delta = 0; Ref_Delta = (time.now()/(15*60)) - REFERENCE_TIME //how many blocks of 15 minutes did we miss? if (Ref_Delta <= MAX_RESERVATIONS) { //make sure we didn't miss more than 250 hours (total reservation storage) RESERVATION_POINTER = RESERVATION_POINTER + Ref_Delta; debug("ResvnPtr_Adjusted",1); } } } void onSerialData() { _gps.onSerialData(); } void loop() { //every second we check which reservation slot we're in and advance the reservation pointer as necessary if (REFERENCE_TIME < (time.now()/(15*60))){ //if true we're now in a new block of reservation time, so update reference time and advance the Reservation Pointer REFERENCE_TIME = (time.now()/(15*60)) //reference time updated if (RESERVATION_POINTER < MAX_RESERVATIONS) { //the pointer isn't at the end of the block yet, increment RESERVATION_POINTER++; } else { //the pointer is at the end of the block (MAX_RESERVATIONS) therefore loop back to 0. RESERVATION_POINTER=0; } } //gps stuff Rmc rmc = Rmc(_gps); //leave this for now; pull out useful data later //if (rmc.parse()) //{ /*Serial.println("3) Recommended Minimum Navigation Information ($GPRMC)"); Serial.println("======================================================"); Serial.print("UTC Time: "); Serial.println(rmc.utcTime); Serial.print("Latitude: "); Serial.println(rmc.latitude); Serial.print("North/SouthIndicator: "); Serial.println(rmc.northSouthIndicator); Serial.print("Longitude: "); Serial.println(rmc.longitude); Serial.print("East/WestIndicator: "); Serial.println(rmc.eastWestIndicator); Serial.print("Speed Over Ground: "); Serial.println(rmc.speedOverGround); Serial.print("Course Over Ground: "); Serial.println(rmc.courseOverGround); Serial.print("Date: "); Serial.println(rmc.date); Serial.print("Magnetic Variation: "); Serial.print(rmc.magneticVariation); Serial.print(" "); Serial.println(rmc.magneticVariationDirection); Serial.print("Mode: "); Serial.println(rmc.mode); Serial.println(""); */ //} ValidGPSPosition = rmc.latitude + ";" + rmc.northSouthIndicator + ";" + rmc.longitude + ";" + rmc.eastWestIndicator + ";" + rmc.utcTime; BatteryVoltageRead = analogRead(A1); BatteryVoltage = (((BatteryVoltageRead * 3.3)/4096)/.26); int counter = 0; unsigned long cardread = 0; //read the input from the card reader and open/close the doors if valid while (RECEIVER.available()) { //Particle.process(); //delay(500); for (int cardident = 0; cardident < 5; cardident++) { counter = (RECEIVER.read()); if (cardident > 0) Serial.print(int(counter), HEX); CARDcurrent[cardident-1] = counter; //Serial.print(" "); } //Serial.println(""); cardread=int(CARDcurrent[0]); cardread=cardread << 8; cardread=cardread+ int(CARDcurrent[1]); cardread=cardread << 8; cardread=cardread+ int(CARDcurrent[2]); cardread=cardread << 8; cardread=cardread+ int(CARDcurrent[3]); CardReadStr = String(cardread, HEX); Serial.println("card string"); Serial.println(cardpublish); bool success; if (String(cardread, HEX)==MasterRFIDCard) { debug("MasterRFIDused",1); Serial.println("ReservationRFIDused"); StatusLEDFn(); if (DoorsOpenBool) { DoorCloseFn(); Serial.println("closing"); } else { DoorOpenFn(); Serial.println("Opening"); } } if ((String(cardread, HEX)==ReservationRFIDCard)) { debug("ReservationRFIDused",1); Serial.println("ReservationRFIDused"); StatusLEDFn(); if (DoorsOpenBool) { DoorCloseFn(); Serial.println("closing"); } else { DoorOpenFn(); Serial.println("Opening"); } } success = Particle.publish("RFIDident", String(cardread, HEX), 0, PUBLIC); if (!success) { Serial.println("failedtopublish"); // get here if event publish did not work } } RECEIVER.flush(); if (Particle.connected()) { Serial.println("Connected!"); } //put in a delay to prevent double reads delay(1000); } //Allow the doors to be opened from Particle Cloud int OpenRelayCmd(String command) { if (command=="on") { DoorOpenFn(); return 1; } else { return -1; } } //Allow the doors to be closed from Particle Cloud int CloseRelayCmd(String command) { if (command=="on") { DoorCloseFn(); return 1; } else { return -1; } } //Allow the status LED to be flashed from Particle Cloud int StatusLEDFlash(String command) { if (command=="on") { StatusLEDFn(); return 1; } else { return -1; } } //Configure car from Particle Cloud int ConfigString(String ConfigmssgArgs){ Serial.println("Config command recieved..."); Serial.println(ConfigmssgArgs); ConfigCmd command; command.extractValues(ConfigmssgArgs); Serial.println("Valid data received..."); Serial.print("MasterRFID: "); Serial.println(command.MasterRFIDStr()); debug(command.MasterRFIDStr(),1); Serial.println("CarType = "); Serial.println(command.CarTypeInt()); MasterRFIDCard=command.MasterRFIDStr(); CarType = command.CarTypeInt(); char MasterRFIDstore[8]; MasterRFIDstore[0] = MasterRFIDCard[0]; MasterRFIDstore[1] = MasterRFIDCard[1]; MasterRFIDstore[2] = MasterRFIDCard[2]; MasterRFIDstore[3] = MasterRFIDCard[3]; MasterRFIDstore[4] = MasterRFIDCard[4]; MasterRFIDstore[5] = MasterRFIDCard[5]; MasterRFIDstore[6] = MasterRFIDCard[6]; MasterRFIDstore[7] = MasterRFIDCard[7]; EEPROM.put(2020, MasterRFIDstore); EEPROM.put(2038, CarType); return 1; } //Store a reservation int ReserveString(String mssgArgs){ Serial.println("command received..."); Serial.println(mssgArgs); ReservationCmd command; command.extractValues(mssgArgs); Serial.println("Valid reservation received..."); Serial.print("UserID: "); Serial.println(command.UserIDStr()); debug(command.UserIDStr(),1); Serial.println("Epoch = "); Serial.println(command.Epoch()); Serial.println("Duration = "); Serial.println(command.DurationInt()); long int ReservationTime = Time.now(); //store current epoch time in ReservationTime = command.Epoch() - ReservationTime; //how many seconds in to the future int ReservedBlocks; //variable for the number of 15m intervals in the future int ReservationDuration; ReservedBlocks = ReservationTime / (60*15); //divide by 15 minute blocks if (FutureBlocks <= MAX_RESERVATION_LENGTH) //check that we're not exceeding the reservation { ReservationDuration = (command.DurationInt() / (60*15)); debug("Reservation length",1); //debug(ReservationDuration,1); //i was here, adding the structure to reserve the EEPROM blocks with the code in a loop. debug("Reservation ok",1); return 1; } else { debug("Reservation too far in the future",1); return -1; } } int HomeCoords(String command){ if (command=="on") { digitalWrite(StatusLED,HIGH); delay(1000); digitalWrite(StatusLED,LOW); return 1; } else { return -1; } } int WipeReservations(String command) { //wipes all reservations, retains MasterRFID and CarType variables char TempMasterRFID[8]; int TempCarType; //status complete if (command=="on") { //flash the LED StatusLEDFn(); //get the fixed variables EEPROM.get(2020, TempMasterRFID); EEPROM.get(2038, TempCarType); debug("EEPROM about to be wiped",1); debug(TempMasterRFID,1); //Wipe the EEPROM clearEEPROM(); //Reset the onboard reservation counter to 0 (assuming EEPROM is wiped) EEPROM.put(2020, TempMasterRFID); EEPROM.put(2038, TempCarType); debug("EEPROM Wipe complete",1); debug(TempMasterRFID,1); return 1; } else { return -1; } //print messages to the console void debug(String message, int value) { char msg [50]; sprintf(msg, message.c_str(), value); Particle.publish("DEBUG", msg); } void DoorOpenFn() //open the doors, set the door status to open { switch (CarType) { case 1: digitalWrite(OpenRelay,HIGH); delay(400); digitalWrite(OpenRelay,LOW); break; case 2: digitalWrite(OpenRelay,HIGH); delay(400); digitalWrite(OpenRelay,LOW); case 3: digitalWrite(OpenRelay,HIGH); delay(400); digitalWrite(OpenRelay,LOW); delay(200); digitalWrite(OpenRelay,HIGH); delay(400); digitalWrite(OpenRelay,LOW); break; default: digitalWrite(OpenRelay,HIGH); delay(400); digitalWrite(OpenRelay,LOW); } DoorsOpenBool = true; debug("Opened Doors",1); //return true; } void DoorCloseFn() //close the doors, set the door open state to false { switch (CarType) { case 1: digitalWrite(OpenRelay,HIGH); delay(400); digitalWrite(OpenRelay,LOW); break; case 2: digitalWrite(CloseRelay,HIGH); delay(400); digitalWrite(CloseRelay,LOW); case 3: digitalWrite(CloseRelay,HIGH); delay(400); digitalWrite(CloseRelay,LOW); delay(200); digitalWrite(CloseRelay,HIGH); delay(400); digitalWrite(CloseRelay,LOW); break; default: digitalWrite(CloseRelay,HIGH); delay(400); digitalWrite(CloseRelay,LOW); } DoorsOpenBool = false; debug("Closed Doors",1); } void StatusLEDFn() //flash the status led once. { digitalWrite(StatusLED,HIGH); delay(1000); digitalWrite(StatusLED,LOW); } char ConvertToHex(char inputcharacter) { char outputchar; switch (int(inputcharacter)) { case 48: //0 outputchar = char(0); break; case 49: //1 outputchar = char(1); break; case 50: //2 outputchar = char(2); break; case 51: //3 outputchar = char(3); break; case 52: //4 outputchar = char(4); break; case 53: //5 outputchar = char(5); break; case 54: //6 outputchar = char(6); break; case 55: //7 outputchar = char(7); break; case 56: //8 outputchar = char(8); break; case 57: //9 outputchar = char(9); break; case 65: //A outputchar = char(10); break; case 66: //B outputchar = char(11); break; case 67: //C outputchar = char(12); break; case 68: //D outputchar = char(13); break; case 69: //E outputchar = char(14); break; case 70: //F outputchar = char(15); break; case 71: //a outputchar = char(10); break; case 72: //b outputchar = char(11); break; case 73: //c outputchar = char(12); break; case 74: //d outputchar = char(13); break; case 75: //e outputchar = char(14); break; case 76: //f outputchar = char(15); break; default: digitalWrite(CloseRelay,HIGH); delay(400); digitalWrite(CloseRelay,LOW); } } void clearEEPROM() { for(int addr = 0; addr < 2047; addr++) { EEPROM.write(addr, 0); } } }