commit 5d7313185083564b17b137979bf2e0b1a0adfc58 Author: James Devine Date: Sat Apr 14 18:12:11 2018 +0200 diff --git a/Open_Close_auto.ino b/Open_Close_auto.ino new file mode 100644 index 0000000..f90960d --- /dev/null +++ b/Open_Close_auto.ino @@ -0,0 +1,545 @@ +// This #include statement was automatically added by the Particle IDE. +#include +#include +#include "Particle.h" + +//note that the number of live reservations in the system is stored in eeprom location 2046 (i.e. the last slot) + +//This is licensed under GPL V3. + +//status: +//master rfid is encoded. +//gps position feedback up and running +//Battery Voltage added. +//working on adding reservations +//homebox function needs writing +//Reservation command works -> prints out data locally from parser; +//Reservation data is now stored in the EEPROM (36 bytes per reservation, starting from byte 0) +//the number of reservations needs to be limited; to 50 - to allow some space for other persistent variables (?) +//This limiting must be added to the code; if reservations > 50; return -1. +//Recalling reservation data mechanism to be done most easily via publish process +//then work on the indexing + 'live' reservation and reservation deletion. +//Somehow reservations need to be archived in a sequential order. Bubble sort? + + +#define RECEIVER SoftSer +#define PROTOCOL SERIAL_8N1 +const int MAX_RESERVATIONS = 48; //allow only 48 reservation at a time. +const int KEY_BUTTONS = 1; // the number of buttons on the key fob, if one button then it cycles relay 1, if 2 buttons 1 is open 2 is close. + +const uint32_t baud = 9600; + +//init GPS device on Serial1 +Gps _gps = Gps(&Serial1); + +//Create a 1ms timer to feed in the GPS data +Timer _timer = Timer(1, onSerialData); + + +int Dummy_Tx; +//Pin assignments for the RFID reader +#define PSS_RX D3 // RX must be interrupt enabled (on Photon/Electron D0/A5 are not) +#define PSS_TX C5 //this pin isn't something we use, I couldn't find a null pin +//#define PSS_TX NULL //here is the null +ParticleSoftSerial SoftSer(PSS_RX, PSS_TX); + +struct RemoteReservation { + char UserID[9]; + int StartDay; + int StartMonth; + int StartYear; + int StartHour; + int StartMinute; + int Duration; +}; + +struct LocalReservation { + char UserID[9]; + int StartDay; + int StartMonth; + int StartYear; + int StartHour; + int StartMinute; + int Duration; + int ResvnNumber; +}; + + +//variable for RFID card reading process +char CARDcurrent[4]; + +//Pin asignments +int OpenRelay = D4; +int CloseRelay = D5; +int StatusLED = D7; + +//boolean to track the door status; actually we'll only use it as a toggle. +bool DoorsOpenBool = false; + + +void clearEEPROM(); // forward declaration +uint16_t EEPROMctr; //eeprom index +uint16_t ReservationCtr; //reservation counter +//Note that the 0th value of the EEPROM is used to store the number of reservations +//Values 1+ store reservations in blocks of 40 bytes. +int ReservationSize = 40; //number of bytes per reservation + + + +//some strings that we'll use for system variables +String ReservationsTable; +String ValidGPSPosition; +int BatteryVoltageRead; +double BatteryVoltage; + +//define a master RFID card that can always open/close the door regardless of reservation status (i.e. for pool manager) +String MasterRFIDCard = "24a10683"; + +class ReservationCmd { + String argument; +public: + void extractValues(String); + String UserIDStr (void) { + return argument.substring(argument.indexOf("UserID=")+7, argument.indexOf("StDy=")); + } + int StartDayInt (void) { + return (argument.substring(argument.indexOf("StDy=")+5, argument.indexOf("StMon="))).toInt(); + } + int StartMonInt (void) { + return (argument.substring(argument.indexOf("StMon=")+6, argument.indexOf("StYr="))).toInt(); + } + int StartYearInt (void) { + return (argument.substring(argument.indexOf("StYr=")+5, argument.indexOf("StHr="))).toInt(); + } + int StartHourInt (void) { + return (argument.substring(argument.indexOf("StHr=")+5, argument.indexOf("StMin="))).toInt(); + } + int StartMinuteInt (void) { + return (argument.substring(argument.indexOf("StMin=")+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; +} + +void debug(String message, int value) { + char msg [50]; + sprintf(msg, message.c_str(), value); + Particle.publish("DEBUG", msg); +} + +void DoorOpenFn() +{ + if (KEY_BUTTONS == 1) { + digitalWrite(OpenRelay,HIGH); + delay(400); + digitalWrite(OpenRelay,LOW); + } + else + { + digitalWrite(OpenRelay,HIGH); + delay(400); + digitalWrite(OpenRelay,LOW); + } + DoorsOpenBool = true; +} + +void DoorCloseFn() +{ + if (KEY_BUTTONS==1) { + digitalWrite(OpenRelay,HIGH); + delay(400); + digitalWrite(OpenRelay,LOW); + } + else + { + digitalWrite(CloseRelay,HIGH); + delay(400); + digitalWrite(CloseRelay,LOW); + } + DoorsOpenBool = false; +} + +void StatusLEDFn() +{ + digitalWrite(StatusLED,HIGH); + delay(1000); + digitalWrite(StatusLED,LOW); +} + +LocalReservation CreateReservation(String CUserID, int CStartDay, int CStartMonth, int CStartYear, int CStartHour, int CStartMinute, int CDuration) +{ + //make a buffer for the reservation + LocalReservation CreateResvn; + + //transfer the data from the function - there's probably a better way to do the string transfer + CreateResvn.UserID[0] = CUserID[0]; + CreateResvn.UserID[1] = CUserID[1]; + CreateResvn.UserID[2] = CUserID[2]; + CreateResvn.UserID[3] = CUserID[3]; + CreateResvn.UserID[4] = CUserID[4]; + CreateResvn.UserID[5] = CUserID[5]; + CreateResvn.UserID[6] = CUserID[6]; + CreateResvn.UserID[7] = CUserID[7]; + CreateResvn.StartDay = CStartDay; + CreateResvn.StartMonth = CStartMonth; + CreateResvn.StartYear = CStartYear; + CreateResvn.StartHour = CStartHour; + CreateResvn.StartMinute = CStartMinute; + CreateResvn.Duration = CDuration; + //send the created variable back + return CreateResvn; +} + +int AddReservation(LocalReservation ReservationToAdd) +{ + //work out the next free spot and put it in that reservation + EEPROM.put((EEPROMctr*ReservationSize), ReservationToAdd); + //update the counter + EEPROMctr++; + ReservationCtr++; + //re-write the counter back to eeprom for synchronisation, which we store at the end of the memory space. + EEPROM.put(2045,EEPROMctr); + EEPROM.put(2043,ReservationCtr); + + //return the reservation number + return EEPROMctr; +} + + + + +void clearEEPROM() { + for(int addr = 0; addr < 2047; addr++) { + EEPROM.write(addr, 0); + } +} + +void setup() +{ + //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("PubPosn", SendPosition); deprecated + Particle.function("MkResvn", ReserveString); + Particle.function("CanResvn", CancelString); + Particle.function("HomeBox", HomeCoords); + Particle.function("PubResvn", ShowReservations); + Particle.function("WipeResvn", WipeReservations); + //define variables + Particle.variable("CarResvn", ReservationsTable); + 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(); + + //initialise the EEPROM counter with the number of reservations that were in memory before poweroff. + EEPROM.get(2045, EEPROMctr); + EEPROM.get(2043, ReservationCtr); + +} + + +void onSerialData() +{ + _gps.onSerialData(); +} + + +void loop() +{ + + + Serial.print("Data[0] = "); Serial.println(_gps.data[0]); + Serial.print("Data[1] = "); Serial.println(_gps.data[1]); + Serial.print("Data[2] = "); Serial.println(_gps.data[2]); + Serial.print("Data[3] = "); Serial.println(_gps.data[3]); + Serial.print("Data[4] = "); Serial.println(_gps.data[4]); + Serial.print("Data[5] = "); Serial.println(_gps.data[5]); + Serial.print("Data[6] = "); Serial.println(_gps.data[6]); + + + Rmc rmc = Rmc(_gps); + 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; + Serial.println(ValidGPSPosition); + BatteryVoltageRead = analogRead(A1); + BatteryVoltage = (((BatteryVoltageRead * 3.3)/4096)/.26); + +int counter = 0; +unsigned long cardread = 0; +String cardpublish; + +while (RECEIVER.available()) + { + 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]); + Serial.println("card raw"); + Serial.println(cardread, HEX); + cardpublish = String(cardread, HEX); + Serial.println("card string"); + Serial.println(cardpublish); + bool success; + success = Particle.publish("RFIDident", String(cardread, HEX), 0, PUBLIC); + if (!success) { + Serial.println("failedtopublish"); // get here if event publish did not work + } + if (String(cardread, HEX)==MasterRFIDCard) { + Serial.println("match"); + StatusLEDFn(); + if (DoorsOpenBool) { + DoorCloseFn(); + Serial.println("closing"); + } + else + { + DoorOpenFn(); + Serial.println("Opening"); + } + } + + } + RECEIVER.flush(); + + if (Particle.connected()) { + Serial.println("Connected!"); + } + delay(1000); +} + + + +int OpenRelayCmd(String command) { + /* Particle.functions always take a string as an argument and return an integer. + Since we can pass a string, it means that we can give the program commands on how the function should be used. + In this case, telling the function "on" will turn the LED on and telling it "off" will turn the LED off. + Then, the function returns a value to us to let us know what happened. + In this case, it will return 1 for the LEDs turning on, 0 for the LEDs turning off, + and -1 if we received a totally bogus command that didn't do anything to the LEDs. + */ + + if (command=="on") { + DoorOpenFn(); + return 1; + } + else { + return -1; + } +} + + + +int CloseRelayCmd(String command) { + /* Particle.functions always take a string as an argument and return an integer. + Since we can pass a string, it means that we can give the program commands on how the function should be used. + In this case, telling the function "on" will turn the LED on and telling it "off" will turn the LED off. + Then, the function returns a value to us to let us know what happened. + In this case, it will return 1 for the LEDs turning on, 0 for the LEDs turning off, + and -1 if we received a totally bogus command that didn't do anything to the LEDs. + */ + + if (command=="on") { + DoorCloseFn(); + return 1; + } + else { + return -1; + } +} + +int StatusLEDFlash(String command) { + /* Particle.functions always take a string as an argument and return an integer. + Since we can pass a string, it means that we can give the program commands on how the function should be used. + In this case, telling the function "on" will turn the LED on and telling it "off" will turn the LED off. + Then, the function returns a value to us to let us know what happened. + In this case, it will return 1 for the LEDs turning on, 0 for the LEDs turning off, + and -1 if we received a totally bogus command that didn't do anything to the LEDs. + */ + + if (command=="on") { + StatusLEDFn(); + return 1; + } + else { + return -1; + } + +} + +/* +//deprecated +int SendPosition(String command){ + if (command=="on") { + //flash the led + StatusLEDFn(); + ValidGPSPosition = _gps.data[4]; + + return 1; + } + else { + return -1; + } +} +*/ + +int ReserveString(String mssgArgs){ + Serial.println("command recieved..."); + Serial.println(mssgArgs); + //oldData = false; + //lastDataTransmitTime = millis(); + ReservationCmd command; + command.extractValues(mssgArgs); + Serial.println("Valid Reservation Recieved..."); + Serial.print("UserID: "); + Serial.println(command.UserIDStr()); + debug(command.UserIDStr(),1); + Serial.println("Start Day = "); + Serial.println(command.StartDayInt()); + Serial.println("Start Month = "); + Serial.println(command.StartMonInt()); + Serial.println("Start Year = "); + Serial.println(command.StartYearInt()); + Serial.println("Start Hour = "); + Serial.println(command.StartHourInt()); + Serial.println("Start Minute = "); + Serial.println(command.StartMinuteInt()); + Serial.println("Duration = "); + Serial.println(command.DurationInt()); + // updateVariables(i, command.mssgText(), command.mssgValue0(), command.mssgValue1()); // Pass decoded variables to function + // badMessage = false; + //now create a temporary reservation record +LocalReservation ResvToInsert; +//and populate it with the values we just parsed + ResvToInsert = CreateReservation(command.UserIDStr(), command.StartDayInt(), command.StartMonInt(), command.StartYearInt(), command.StartHourInt(), command.StartMinuteInt(), command.DurationInt()); +//and now insert it into the EEPROM +//initialise a dummy variable to hold the reservation number (it'll be >1 by the time it comes back) +int ReservationNumberAdded = 0; +//use the return from AddReservation to pass the reservation number post insertion + ReservationNumberAdded = AddReservation(ResvToInsert); +//return the reservation number. +return ReservationNumberAdded; +} + +int CancelString(String command){ + if (command=="on") { + digitalWrite(StatusLED,HIGH); + delay(1000); + digitalWrite(StatusLED,LOW); + return 1; + } + else { + return -1; + } +} + +int HomeCoords(String command){ + if (command=="on") { + digitalWrite(StatusLED,HIGH); + delay(1000); + digitalWrite(StatusLED,LOW); + return 1; + } + else { + return -1; + } +} + +int ShowReservations(String command){ + //input + //is the reservation in the table? (i.e. is it higher than the max resvn. number, if so fail) + if (command.toInt()<= EEPROMctr) { + //Create a buffer to store the recalled data + LocalReservation RetRes; + //extract it from the EEPROM + EEPROM.get(((command.toInt()-1)*ReservationSize), RetRes); + //Now format it as a string + const int STRING_BUF_SIZE = 9; + char stringBuf[STRING_BUF_SIZE]; + stringBuf[sizeof(stringBuf) - 1] = 0; // make sure it's null terminated + String ResOutBuf; + // Initialize a String object from the buffer + String str(stringBuf); + str = String(RetRes.UserID); + //Print it out + Serial.printlnf("ReservationNum=%d, UserID=%s, Day=%d, Month=%d, Year=%d, Hour=%d, Minute=%d, Duration=%d, sizeof(RetRes)=%d", command.toInt(), str.c_str(), RetRes.StartDay, RetRes.StartMonth, RetRes.StartYear, RetRes.StartHour, RetRes.StartMinute, RetRes.Duration, sizeof(RetRes)); + //sprintf(ResOutBuf,"ReservationNum=%d, UserID=%s, Day=%d, Month=%d, Year=%d, Hour=%d, Minute=%d, Duration=%d, sizeof(RetRes)=%d", command.toInt(), str.c_str(), RetRes.StartDay, RetRes.StartMonth, RetRes.StartYear, RetRes.StartHour, RetRes.StartMinute, RetRes.Duration, sizeof(RetRes))); + debug(str.c_str(),1); + return 1; + } + else { + return -1; + } +} + +int WipeReservations(String command) { + //wipes all reservations + //status complete + if (command=="on") { + //flash the LED + StatusLEDFn(); + //Wipe the EEPROM + clearEEPROM(); + //Reset the onboard reservation counter to 0 (assuming EEPROM is wiped) + EEPROMctr = 0; + ReservationCtr = 0; + EEPROM.put(2043, ReservationCtr); + EEPROM.put(2045, EEPROMctr); + return 1; + } + else { + return -1; + } + +} \ No newline at end of file