/* SMS Gateway This node works as a SMS gateway, receiving and sending SMS messages at the request of other nodes It needs to have a fixed ID to be able to be addessed within a MySensors network. As it is easy to remember I chose the ID 123. Once the node is sent an initiating SMS, it will respond to the received calling number as it's "owner". This way you do not need to hardcode your number in the sketch. But to make it operational, you will need to send at least 1 SMS to it with the prededfined text MYPASSWORD (see below in the definitions, you set your own string). The node should respond with CONFIRMOWNER, you can set this string in de definitions below. The allow another phone to control the node and receive messages, the "owner" needs to release the node by sending a SMS with the text MYRELEASEWORD. Again you can set this string in the definitions below. The node will then again become ownerless and ignore all messages and SMS's. If the node has an owner and another phone sends a message to the node, it will inform the owner of a "take over attempt". You can sent V_TEXT to other nodes in your network by using the following format: "N021:xxx.any.25.characters.xxx" Anything sent after the "N021:" will be sent to the node with address 021. A V_TEXT has a maximum length of 25 charters. Adding the node address make the SMS length 30 charters. The SMS buffer is 31 characters long. A node address can have a value 000 (the controller) and any value between 001 and 254. Values higher then 254 are ignored and nothing will be done with the received SMS, apart from informing the controller that this SMS was received. Sending a SMS to the SMS Gateway with the address of the SMS Gateway itself will result in the SMS Gateway sending the same SMS back. So if you send the following SMS to your SMS Gateway: "N123:Are you there ?" You will receive a SMS with the content ""N123:Are you there ?" What is happening ? The SMS Gateway receives a SMS adressed to the node 123, the SMS Gateway sends a copy of the received SMS to the controller and sends a message with content "Are you there ?" to the node 123. Since node 123 is the SMS Gateway receiving a message, it will pass on this message as a SMS to the owner in the format specified earlier. So a text SMS is sent with content "N123:Are you there?" If a node sends a V_TEXT "Garagedoor is opened" to the SMS Gateway, the SMS gateway will pass this on to the owner's GSM in the format below: "N011:Garagedoor is opened" Address from the node sending the text is sent first (in this case "N011:"), and then the contents of the V_TEXT itself is appended ("Garagedoor is opened"). You need a SIM800L module hooked to an atmega328 based MySensor node via softserial port. This sketch was made for my AC capable board wich allows use of a SIM800L module. More on that board on the forum at this location: http://forum.mysensors.org/topic/2374/50mm-x-50mm-board-with-different-powering-options/10 or https://www.openhardware.io/view/11/ACDCBatteries-capable-atmega328p-board Nodes that want to inform an GSM owner via the SMS Gateway need to have the capacity to send a V_TEXT. Even nodes that sleep most of the time can send messages to any other node. You just need to define a V_TEXT message type. Nodes that need to react to SMS messages with instructions, need to be able to receive V_TEXT and need to be reachable (always on). So battery based nodes will not be the best candidates as actuators based on SMS. This example code is in the public domain. modified 10 oktober 2015 version 1.0 modified 6 january 2016 version 1.1 modified 15 january 2016 version 1.2 by GertSanders on the MySensors forum Uses the Adafruit FONA library to drive the SIM800L, this need to be included in your library to compile this sketch. To do: create a buffer structure to handle several incoming messages from nodes or phone. */ #define SKETCHNAME "SMSGatewayNode" #define SKETCHVERSION "1.2" // Enable debug prints to serial monitor //#define MY_DEBUG //#define MY_DEBUG_VERBOSE //#define MY_BAUD_RATE 57600 #define MY_NODE_ID 123 #define GATEWAY_ID 0 // Enable and select radio type attached #define MY_RADIO_NRF24 //#define MY_RADIO_RFM69 // defines for sensors //#define BATTERYLEVEL 0 #define SMSTEXT 1 // Enable repeater functionality for this node //#define MY_REPEATER_FEATURE // All includes #include #include #include #include // Pin definitions and other defines #define FONA_RX 2 #define FONA_TX 3 #define FONA_RST 4 #define defaultledpin 8 // usually this is pin 13, but better to use another digital pin // constants #define MAXSMSLENGTH 30 // No need for longer messages from GSM's as V_TEXT is limiting what can be sent to the other nodes. Node addresses are maximum 000 -> 255 #define MAXMSGLENGTH 25 // limiting factor is the payload of V_TEXT, which is 25 chars. Nodes will never send you more then this in 1 message. #define MAXNUMBERLENGTH 12 // based on "plus" sign, a country prefix and national number. Example in Belgium: +32 123 45 67 #define MAXNODEID 254 // highest value a node ID can have // following strings need to be set by you, maximum length is 30 CHARS. #define MYPASSWORD "Password" // replace this with your preferred passphrase to gain ownership #define CONFIRMOWNER "I am yours" // replace this string if your partner is of the untrusting type ;-) #define MYRELEASEWORD "I release you" // replace this with your preferred release confirmation // variables bool KnowMyOwner = false; bool MsgReceived = false; uint16_t smslen = 0; int8_t smsnum = 0; int bufferindex = 0; char smsbuffer[MAXSMSLENGTH + 1] = ""; char msgbuffer[MAXMSGLENGTH + 1] = ""; char nodemsgbuffer[MAXMSGLENGTH + 1] = ""; char nodeaddress[5]; char Numbers[11] = "0123456789"; char myOwnersNumber[MAXNUMBERLENGTH + 1] = ""; char ReceivedNumber[MAXNUMBERLENGTH + 1] = ""; // Instantiate objects MyMessage SMSMsg(SMSTEXT, V_TEXT); Adafruit_FONA fona = Adafruit_FONA(FONA_RST); SoftwareSerial fonaSS = SoftwareSerial(FONA_TX, FONA_RX); SoftwareSerial *fonaSerial = &fonaSS; ///////////////////////////////////////////////////////////////////////// // supporting functions. ///////////////////////////////////////////////////////////////////////// void blipled() { digitalWrite(defaultledpin, HIGH); // turn the LED on (HIGH is the voltage level) wait(3); // wait for a second digitalWrite(defaultledpin, LOW); // turn the LED off by making the voltage HIGH wait(100); // wait for a second digitalWrite(defaultledpin, HIGH); // turn the LED on (HIGH is the voltage level) wait(3); // wait for a second digitalWrite(defaultledpin, LOW); // turn the LED off by making the voltage HIGH } ///////////////////////////////////////////////////////////////////////// int GetNodeID() { int TheAddress = -1; TheAddress = ((smsbuffer[1] - 48) * 100) + ((smsbuffer[2] - 48) * 10) + (smsbuffer[3] - 48); return TheAddress; } ///////////////////////////////////////////////////////////////////////// bool IsAddressingNode() { if (strlen(smsbuffer) > 5) { // at least something to check if ((smsbuffer[0] == 'N') && (smsbuffer[4] == ':') && (strchr(Numbers, smsbuffer[1]) != NULL) && (strchr(Numbers, smsbuffer[2]) != NULL) && (strchr(Numbers, smsbuffer[3]) != NULL)) { #ifdef MY_DEBUG Serial.print("Message addressed to: "); Serial.println(GetNodeID()); #endif if (GetNodeID() <= MAXNODEID) return true; else return false; } else { return false; } } else { return false; } } ///////////////////////////////////////////////////////////////////////// char* GetMessageContent(char incomingsmsbuffer[MAXSMSLENGTH]) { strcpy(nodemsgbuffer, ""); for (int i = 5; i < MAXSMSLENGTH; i++) { nodemsgbuffer[i - 5] = incomingsmsbuffer[i]; } return nodemsgbuffer; } ///////////////////////////////////////////////////////////////////////// void presentation() { // Send the sketch version information to the gateway and Controller sendSketchInfo(SKETCHNAME, SKETCHVERSION); wait(500); // node ID is 123 present(SMSTEXT, S_INFO); wait(500); } ///////////////////////////////////////////////////////////////////////// void receive(const MyMessage & message) { #ifdef MY_DEBUG Serial.print(F("Incoming from sensor: ")); Serial.print(message.sensor); #endif if ((message.type == V_TEXT) and (!MsgReceived)) { sprintf(smsbuffer, "N%d:", message.sender); strcat(smsbuffer, message.getString()); strcpy(msgbuffer, message.getString()); MsgReceived = true; #ifdef MY_DEBUG Serial.print(F(", New text: ")); Serial.print(message.getString()); #endif // send a copy of the received message to the controller } #ifdef MY_DEBUG Serial.println(); #endif } // end: void receive() ///////////////////////////////////////////////////////////////////////// void clearSMSmemory() { int8_t smsnum = fona.getNumSMS(); while (smsnum > 0) { fona.deleteSMS(smsnum); smsnum--; } } ///////////////////////////////////////////////////////////////////////// // the setup function runs once when you press reset or power the board ///////////////////////////////////////////////////////////////////////// void setup() { KnowMyOwner = false; MsgReceived = false; SMSMsg.setDestination(GATEWAY_ID); // initialize digital LEDpin as an output. pinMode(defaultledpin, OUTPUT); digitalWrite(defaultledpin, HIGH); // turn the LED on by making the voltage HIGH #ifdef MY_DEBUG Serial.println(F("SIM800L basic test")); Serial.println(F("Initializing....(May take 3 seconds)")); Serial.println(F("starting SIM800L")); #endif fonaSerial->begin(4800); if (! fona.begin(*fonaSerial)) { #ifdef MY_DEBUG Serial.println(F("Couldn't find SIM800L")); #endif while (1); } #ifdef MY_DEBUG Serial.println(F("SIM800L is OK")); #endif digitalWrite(defaultledpin, LOW); // turn the LED off by making the voltage LOW wait(250); // wait for a bit digitalWrite(defaultledpin, HIGH); // turn the LED on by making the voltage HIGH #ifdef MY_DEBUG // Print SIM card IMEI number. char imei[15] = {0}; // MUST use a 16 character buffer for IMEI! uint8_t imeiLen = fona.getIMEI(imei); if (imeiLen > 0) { Serial.print(F("SIM card IMEI: ")); Serial.println(imei); } #endif smsnum = fona.getNumSMS(); if (smsnum < 0) { #ifdef MY_DEBUG Serial.println(F("Could not read # SMS")); #endif } else { #ifdef MY_DEBUG Serial.print(smsnum); Serial.println(F(" SMS's on SIM card!")); #endif } clearSMSmemory(); smsnum = fona.getNumSMS(); if (smsnum == 0) { #ifdef MY_DEBUG Serial.println(F("SMS Memory cleared")); #endif } digitalWrite(defaultledpin, LOW); // turn the LED off by making the voltage LOW } /////////////////////////////////////////////////////// // the loop function runs over and over again forever /////////////////////////////////////////////////////// void loop() { blipled(); // sensor.request(SMSTEXT, V_TEXT, 0); wait(500); // wait for a second and proces messages #ifdef MY_DEBUG Serial.print(F("Owner's number: ")); Serial.println(myOwnersNumber); Serial.print(F("Last received number: ")); Serial.println(ReceivedNumber); #endif smsnum = fona.getNumSMS(); if (smsnum < 0) { #ifdef MY_DEBUG Serial.println(F("Could not read # SMS")); #endif } #ifdef MY_DEBUG Serial.println(F("Process incoming messages")); #endif if (MsgReceived && KnowMyOwner) { // a node sent us V_TEXT and we know the owner, so we can send an SMS SMSMsg.setDestination(GATEWAY_ID); send(SMSMsg.set(smsbuffer)); if (fona.sendSMS(myOwnersNumber, smsbuffer)) { // SMS was sent to owner MsgReceived = false; strcpy(smsbuffer, ""); strcpy(msgbuffer, ""); } else { // We do not know the owner, then ignore this message, we know the owner but no message was received, so just to be safe: MsgReceived = false; strcpy(msgbuffer, ""); } } if (smsnum > 0) { // we received an SMS and now need to check it #ifdef MY_DEBUG Serial.print(smsnum); Serial.println(F(" SMS received")); #endif if (! fona.readSMS(smsnum, smsbuffer, MAXSMSLENGTH, &smslen)) { // could not read SMS #ifdef MY_DEBUG Serial.println(F("Failed to get SMS!")); #endif } else { // We could read the SMS, now retreive sms to smsbuffer and number of origin fona.getSMSSender(smsnum, ReceivedNumber, MAXNUMBERLENGTH); #ifdef MY_DEBUG Serial.print(F("***** SMS #")); Serial.print(smsnum); Serial.print(F(" (")); Serial.print(smslen); Serial.println(F(") bytes *****")); Serial.println(smsbuffer); Serial.print(F("from: ")); Serial.println(ReceivedNumber); Serial.println(F("***************************")); Serial.print(F("Owner is known: ")); if (KnowMyOwner) Serial.println(F("yes")); else Serial.println(F("no")); Serial.println(); #endif if (!KnowMyOwner) { // no owner, so check request for ownership if (strcmp(smsbuffer, MYPASSWORD) == 0) { // valid question for ownership strcpy(smsbuffer, "OwnReq by: "); strcat(smsbuffer, ReceivedNumber); send(SMSMsg.set(smsbuffer)); KnowMyOwner = true; strcpy(myOwnersNumber, ReceivedNumber); // from now on we can respond to owner #ifdef MY_DEBUG Serial.print(F("Request for ownership by ")); Serial.println(myOwnersNumber); #endif strcpy(smsbuffer, CONFIRMOWNER); if (fona.sendSMS(myOwnersNumber, smsbuffer)) { // send confirmation to new owner strcpy(smsbuffer, "Owner is informed."); SMSMsg.setDestination(GATEWAY_ID); send(SMSMsg.set(smsbuffer)); #ifdef MY_DEBUG Serial.print(smsbuffer); Serial.print(": "); Serial.println(myOwnersNumber); #endif } else { // could not send confirmation to new owner strcpy(smsbuffer, "Owner is not informed."); SMSMsg.setDestination(GATEWAY_ID); send(SMSMsg.set(smsbuffer)); } strcpy(smsbuffer, ""); } else { // no owner and no valid request, so ignore and log in controller strcpy(smsbuffer, "Attempt by: "); strcat(smsbuffer, ReceivedNumber); SMSMsg.setDestination(GATEWAY_ID); send(SMSMsg.set(smsbuffer)); strcpy(smsbuffer, ""); } } if (KnowMyOwner) { // now we can accept SMS messages but we need to check the origin of the SMS if (strcmp(myOwnersNumber, ReceivedNumber) == 0) { // this is the owner talking, so handle his SMS #ifdef MY_DEBUG Serial.println(); Serial.print(F("Owner is talking: ")); Serial.println(smsbuffer); Serial.println(); #endif if (strcmp(smsbuffer, MYRELEASEWORD) == 0) { strcpy(smsbuffer, "Released by "); strcat(smsbuffer, ReceivedNumber); SMSMsg.setDestination(GATEWAY_ID); send(SMSMsg.set(smsbuffer)); strcpy(smsbuffer, "I am released."); if (fona.sendSMS(myOwnersNumber, smsbuffer)) { strcpy(smsbuffer, "Owner is informed."); SMSMsg.setDestination(GATEWAY_ID); send(SMSMsg.set(smsbuffer)); } else { strcpy(smsbuffer, "Owner is not informed."); SMSMsg.setDestination(GATEWAY_ID); send(SMSMsg.set(smsbuffer)); } // clean up buffers, we are done and need to get ready for new owner request, ignore further messages KnowMyOwner = false; MsgReceived = false; strcpy(myOwnersNumber, ""); strcpy(ReceivedNumber, ""); strcpy(smsbuffer, ""); strcpy(msgbuffer, ""); } if (strcmp(smsbuffer, "Blip") == 0) { // handle the command to light the defaultled. This could be anything else of course digitalWrite(defaultledpin, HIGH); // turn the LED on (HIGH is the voltage level) wait(2000); // wait for two seconds digitalWrite(defaultledpin, LOW); // turn the LED off by making the voltage LOW strcpy(smsbuffer, "R: Blip"); SMSMsg.setDestination(GATEWAY_ID); send(SMSMsg.set(smsbuffer)); strcpy(smsbuffer, ""); } // and here we put the code to pass the message on to another node // smsbuffer contains the first 30 characters of the SMS received. // if a message starts with Nxxx:1234567890123456789012345 then you need to pass on the content to node Nxxx // the node needs to be able to handle the incoming V_TEXT message if (IsAddressingNode()) // check if sms is in Node addressing format { int NodeID = GetNodeID(); #ifdef MY_DEBUG Serial.println(); Serial.print("Message for node "); Serial.print(NodeID); Serial.print(" is treated: "); Serial.println(GetMessageContent(smsbuffer)); Serial.println(); #endif // send a copy of the incoming message to the controller SMSMsg.setDestination(GATEWAY_ID); send(SMSMsg.set(smsbuffer)); // send the stripped message to the addressed node SMSMsg.setDestination(NodeID); SMSMsg.set(GetMessageContent(smsbuffer)); send(SMSMsg); strcpy(smsbuffer, ""); } // finally we mention to the controller all the SMS messages we received and did not treat. if (strcmp(smsbuffer, "") != 0) { // so we still got something to do #ifdef MY_DEBUG Serial.println(smsbuffer); Serial.println(); #endif //strcpy(smsbuffer, "Received crap from owner."); SMSMsg.setDestination(GATEWAY_ID); send(SMSMsg.set(smsbuffer)); strcpy(smsbuffer, ""); } } else { // not the owner trying to get access strcpy(smsbuffer, "Hack by:"); strcat(smsbuffer, ReceivedNumber); SMSMsg.setDestination(GATEWAY_ID); send(SMSMsg.set(smsbuffer)); #ifdef MY_DEBUG Serial.print(F("Hijack attempt by: ")); Serial.println(ReceivedNumber); #else if (fona.sendSMS(myOwnersNumber, smsbuffer)) { strcpy(smsbuffer, "Owner is informed."); SMSMsg.setDestination(GATEWAY_ID); send(SMSMsg.set(smsbuffer)); } else { strcpy(smsbuffer, "Owner could not be informed."); SMSMsg.setDestination(GATEWAY_ID); send(SMSMsg.set(smsbuffer)); } #endif } } } clearSMSmemory(); } }