This device will send a SMS to a list of subscribers, when the powerline fails. It is used at ETH Hönggerberg, because
a power failure shuts down a lot of (critical) systems (UPS just bridges a gap of minutes). Usually friday night. Sharp tongues would
say that engineers build such apparatus to get a girl's phone number :-)
This Shield / Project is no longer in service, as 2G has been shut down in Switzerland by the
end of April 2021. The yet unpublished replacement is "Dial S on Shutdown 3G".
This project is a Hack-together of an Arduino MEGA (others may need modifications), a GSM-shield, the Shield "LEO"
and a powerline-good detection circuit. A powerful battery (UPS) as well as an lcd
and some test-buttons (not used) round it up. The diagram below shows the building blocks.
✈ Powerline Detector
The powerline detection circuit consists mainly of an optocoupler (e.g. 4N32). The only trick here is the use of a capacitor (in series with a resistor)
to limit the current. We used a 220 nF, 630 V type, which has an impedance of almost 15 kΩ @ 50 Hz. We assume the line frequency to be constant.
The so generated pulses are used (on the shield "LEO") to discharge a capacitor, beeing charged by a resistor to + 5V.
✈ Shield "LEO"
This shield is mainly a central connection unit. You may attach an lcd (16x4). It is also home to a beeper and an eeprom (24LC256)
for the phonebook (you may assemble two of them). The phonebook is updated via USB, by that you have more control on the subscriber data.
It is possible to send test-sms in order for the (prepaid) sim card not to become a "sleeper". The version before used subscription via
sms, but that has turned out to be not that clear for the operator. The board also has an RTC (DS1307Z) which mayst be updated via
a request to the gsm-shield.
The complete management is done via USB
✈ Arduino Sketch - The Code
Double click on code to select ...
/* /////////////////////////////////////////////////////////////////////
ARDUINO/Genuino MEGA Sketch for "LEO", Dial "S" on Shutdown
Software Version 2.0,
20.07.2017, Alexander C. Frank,
HELPFUL WEBSITES:
1) http://www.hobbytronics.co.uk/arduino-external-eeprom
//////////////////////////////////////////////////////////////////////*/
#include <Wire.h>
#include <LiquidCrystal.h>
// RS, E, D4, D5, D6, D7
LiquidCrystal lcd(9, 8, 6, 5, 4, A1);
byte Antenna[8] = {
B011111,
B010101,
B001010,
B000100,
B000100,
B000100,
B000100,
B000100,
};
byte PowerDown[8] = {
B011100,
B000100,
B000100,
B000100,
B000100,
B000100,
B000111,
B000000
};
byte PowerUp[8] = {
B000111,
B000100,
B000100,
B000100,
B000100,
B000100,
B111100,
B000000
};
// Include the GSM library
#include <GSM.h>
#define PINNUMBER "7287"
const int BufferLength = 20 ;
char remoteNumb[BufferLength+1];
char remoteName[BufferLength+1];
char txtMsg[] = "Greetings ! We apologise, but it looks like there is a
severe problem regarding power grid availability. Sensor at
HPF-D16 :-( :-(";
char tstMsg[] = "Greetings ! This is just a general connectivity test.
If you see it, everything is fine. If not, please visit the
Gadgets Lab :-) :-)";
// initialize the library instance
GSM gsmAccess;
GSM_SMS sms;
#include <GSM3ShieldV1DirectModemProvider.h>
GSM3ShieldV1DirectModemProvider modemAccess;
String reply ;
float PREPAIDCHF = 10.0 ;
String CARRIER ;
String RECEIVERSIGNALSTRENGTH ;
unsigned long Last_ALARM_off = 0 ;
unsigned long Last_ALARM_on = 0 ;
unsigned long Last_SMS_out = 0 ;
// Save data variables
String IMEI = "";
// serial monitor result messages
String errortext = "ERROR";
volatile bool isrTriggered = false ;
volatile bool ALARM = false ;
bool StatusOLD = false ;
const int BEEP = 13 ;
const int PageSize = 256 ;
char Buffy[BufferLength+1] ; // holds User Input from Serial
byte byteRead ;
int EEPROMADR = 0x50 ; // THE 24C01, HOLDING SENSOR INFORMATION
int eeaddress = 0 ;
// 1 kBit 128 Bytes 3 names
// 2 kBit 256 Bytes 6 Names <<< TESTMODE
// 4 kBit 512 Bytes 12 Names
// 8 kBit 1024 Bytes 25 Names
// 16 kBit 2048 Bytes 51 Names
// 32 kBit 4096 Bytes 102 Names
// 64 kBit 8192 Bytes 204 Names
// 128 kBit 16384 Bytes 408 Names
// 256 kBit 32768 Bytes 916 Names
const byte NameOffset = 0x00 ;
const byte NameLength = 0x10 ;
const byte CountryOffset = NameOffset + NameLength ;
const byte CountryLength = 0x03 ;
const byte NumberOffset = CountryOffset + CountryLength ;
const byte NumberLength = 0x10 ;
const byte DatasetLength = NameLength + CountryLength + NumberLength ;
const int EEPROMSIZE = 256 - DatasetLength ;
const char Version[] = "1807.2560" ; // DDMM.YYYY
byte data ;
unsigned int DeleteWhat = 99 ;
int aux ;
unsigned int MaximumUsers = EEPROMSIZE / DatasetLength ;
void setup()
{
Wire.begin(); // join i2c bus (address optional for master)
Serial.begin(115200); // start serial for output
print_title();
lcd.begin(16, 4);
lcd.clear();
lcd.setCursor(0,0); lcd.print("DIAL S FOR");
lcd.setCursor(0,1); lcd.print("SHUTDOWN V3.X");
lcd.setCursor(0,2); lcd.print("QUANTUMOPTICS");
lcd.setCursor(0,3); lcd.print("28.07.2017");
delay(1000);
lcd.clear();
pinMode(BEEP, OUTPUT);
digitalWrite(BEEP, LOW);
// SPECIAL CHARACTER DEFINITION
lcd.createChar(0, Antenna);
lcd.createChar(1, PowerDown);
lcd.createChar(2, PowerUp);
// connection state
boolean notConnected = true;
// Start GSM shield
// If your SIM has PIN, pass it as a parameter of begin() in quotes
while (notConnected)
{
if (gsmAccess.begin(PINNUMBER) == GSM_READY)
{
notConnected = false;
}
else
{
Serial.println("Not connected");
delay(1000);
}
}
Serial.println("GSM initialised");
Serial.println("Ask for Credit ... ");
reply = "Ooops, no answer ?!?" ;
if (gsmAccess.getStatus() == GSM_READY)
{
modemAccess.writeModemCommand("AT+CMGF=0",1000);
reply = modemAccess.writeModemCommand("AT+CUSD=1,\"*130#\"",5000);
modemAccess.writeModemCommand("AT+CMGF=1",1000);
}
// String reply="+CUSD: 2, \"Ihr Restguthaben ist CHF 35,70\",15";
Serial.println(reply);
Serial.println("===============================================");
// initialize timer1
noInterrupts(); // disable all interrupts
TCCR1A = 0;
TCCR1B = 0;
TCNT1 = 34286; // preload timer 65536-16MHz/256/2Hz
TCCR1B |= (1 << CS12); // 256 prescaler
TIMSK1 |= (1 << TOIE1); // enable timer overflow interrupt
interrupts(); // enable all interrupts
print_options();
}
ISR(TIMER1_OVF_vect)
{
TCNT1 = 34286; // preload timer
isrTriggered = true ;
}
// /////////////////////////////////////////////////////////////
// Prints a list of Options
// /////////////////////////////////////////////////////////////
void print_options()
{
Serial.print(" >>> WAS GUCKST DU ???\n");
Serial.print("-----------------------------------------------\n");
Serial.print("[s] Show complete phonebook\n");
Serial.print("[e] show Eeprom contents\n");
Serial.print("[f] Format phonebook\n");
Serial.print("[d] Delete entry\n");
Serial.print("[a] Add entry\n");
Serial.print("-----------------------------------------------\n");
Serial.print("[b] test Beeper\n");
Serial.print("[t] Test SMS\n");
Serial.print("-----------------------------------------------\n");
}
// /////////////////////////////////////////////////////////////
// Prints the title block when program first starts.
// /////////////////////////////////////////////////////////////
void print_title()
{
Serial.println("===============================================");
Serial.println("Welcome to Dial S on Shutdown Phonebook Manager");
Serial.print("Software Version : "); Serial.println(Version);
Serial.println("by Alexander C. Frank, ETH QUANTUMOPTICS 2017");
Serial.println("===============================================");
}
// /////////////////////////////////////////////////////////////
// Clears the Input Buffer
// /////////////////////////////////////////////////////////////
void InitBuffy()
{
for (int i=0; i<20; i++)
Buffy[i] = 32 ; // SPACE
}
// /////////////////////////////////////////////////////////////
// Read User Input from Serial
// /////////////////////////////////////////////////////////////
void ReadUserInput()
{
int pointer = 0 ;
boolean ende = false ;
while ( ( Serial.available() ) || ( ende == false ) )
{
if(isrTriggered)
{
TaskScheduler();
isrTriggered = false;
}
byteRead = Serial.read();
if ( byteRead == 10 ){ ende = true ; }
if ( byteRead < 255 )
{
Buffy[pointer] = byteRead ;
if ( pointer < 19 ) pointer++ ;
}
}
}
// /////////////////////////////////////////////////////////////
// WRITE A SINGLE BYTE TO THE EEPROM
// /////////////////////////////////////////////////////////////
void writeEEPROMbyte(unsigned int eeaddress, byte data )
{
Wire.beginTransmission(EEPROMADR);
Wire.write((int)(eeaddress >> 8)); // MSB
Wire.write((int)(eeaddress & 0xFF)); // LSB
Wire.write(data);
Wire.endTransmission();
delay(5);
}
// /////////////////////////////////////////////////////////////
// READ A SINGLE BYTE FROM THE EEPROM
// /////////////////////////////////////////////////////////////
byte readEEPROMbyte( unsigned int eeaddress )
{
byte rdata = 0xFF;
Wire.beginTransmission(EEPROMADR);
Wire.write((int)(eeaddress >> 8)); // MSB
Wire.write((int)(eeaddress & 0xFF)); // LSB
Wire.endTransmission();
Wire.requestFrom(EEPROMADR,1);
if (Wire.available()) rdata = Wire.read();
return rdata;
}
void readName( unsigned int nummer )
{
// EMPTY STRING
for ( int a = 0 ; a < BufferLength ; a++ ) remoteName[a] = '\0' ;
for ( int a = 0 ; a < NameLength ; a++ )
{
remoteName[a] = readEEPROMbyte( nummer * DatasetLength
+ NameOffset + a );
}
for ( int a = NameLength ; a < BufferLength ; a++ )
{
remoteName[a] = 0x20;
}
}
void readNumber( unsigned int nummer )
{
char aux ;
// EMPTY STRING
for ( int a = 0 ; a < BufferLength ; a++ ) remoteNumb[a] = '\0' ;
remoteNumb[0] = readEEPROMbyte( nummer * DatasetLength
+ CountryOffset + 0 );
remoteNumb[1] = readEEPROMbyte( nummer * DatasetLength
+ CountryOffset + 1 );
remoteNumb[2] = readEEPROMbyte( nummer * DatasetLength
+ CountryOffset + 2 );
for ( int a = 0 ; a < NumberLength ; a++ )
{
aux = readEEPROMbyte( nummer * DatasetLength + NumberOffset + a );
if (aux > 0x20) remoteNumb[a+3] = aux ;
}
}
// /////////////////////////////////////////////////////////////
// DELETE ENTRY
// /////////////////////////////////////////////////////////////
void deleteUSER (unsigned int WhichOne )
{
unsigned int StartPointer = WhichOne * DatasetLength ;
unsigned int EndPointer = EEPROMSIZE + DatasetLength ;
byte data = 0x00 ;
for (int m = StartPointer ; m < EndPointer ; m++)
{
data = readEEPROMbyte( m + DatasetLength );
writeEEPROMbyte( m , data );
}
Serial.println("\nO.K.\n\n");
}
// /////////////////////////////////////////////////////////////
// ADD ENTRY
// /////////////////////////////////////////////////////////////
void addUSER ( )
{
// FIND A FREE PLACE IN THE PHONEBOOK : FIRST LETTER IN THE NAME
// IS A SPACE, aka 32 DEC or 0x20
unsigned int POsition = MaximumUsers + 1;
unsigned int FREEposition = MaximumUsers ;
byte data = 0x00 ;
do
{
POsition -= 1;
data = readEEPROMbyte( (POsition * DatasetLength) + NameOffset );
// Serial.print("Position : ");Serial.print(POsition,DEC);
Serial.print("Data : ");Serial.println(data,HEX);
if ( data == 0x20 )
{
FREEposition = POsition ;
}
}
while ( POsition > 0 ) ;
if ( FREEposition < MaximumUsers )
{
// THERE IS SPACE IN THE PHONEBOOK
// /////////////////////////////////////////////////////////////////////
// ASK FOR NAME
Serial.print("\nName : ");
InitBuffy();
ReadUserInput();
aux = -1;
do
{
aux += 1 ;
writeEEPROMbyte( (( FREEposition * DatasetLength)
+ NameOffset + aux ), Buffy[aux] ) ;
}
while (( aux < NameLength ) && ( Buffy[aux] != 0x20 ) );
// /////////////////////////////////////////////////////////////////////
// ASK FOR COUNTRY CODE
Serial.print("\nCountry Code : ");
InitBuffy();
ReadUserInput();
aux = -1;
do
{
aux += 1 ;
writeEEPROMbyte( (( FREEposition * DatasetLength)
+ CountryOffset + aux ), Buffy[aux] ) ;
}
while (( aux < CountryLength ) && ( Buffy[aux] != 0x20 ) );
// /////////////////////////////////////////////////////////////////////
// ASK FOR PHONE NUMBER
Serial.print("\nPhone Number : ");
InitBuffy();
ReadUserInput();
aux = -1;
do
{
aux += 1 ;
writeEEPROMbyte( (( FREEposition * DatasetLength)
+ NumberOffset + aux ), Buffy[aux] ) ;
}
while (( aux < NumberLength ) && ( Buffy[aux] != 0x20 ) );
}
else
{
// PHONEBOOK FULL
Serial.println("Oooops - looks like this thing is already full.\n");
}
Serial.println("\nO.K.\n\n");
}
// /////////////////////////////////////////////////////////////
// READ AND DISPLAY ENTIRE EEPROM
// /////////////////////////////////////////////////////////////
void ReadEeprom()
{
for (int address = 0; address < EEPROMSIZE; address+=8)
{
if (address<256) Serial.print("0");
if (address<16) Serial.print("0");
Serial.print(address,HEX);
Serial.print("\t");
// One row, HEX
for (int add = 0; add < 8; add++)
{
data = readEEPROMbyte( address+add );
if (data<16) Serial.print("0");
Serial.print(data, HEX);
Serial.print(" ");
}
Serial.print("\n");
}
Serial.println("\nO.K.\n\n");
}
// /////////////////////////////////////////////////////////////
// READ AND DISPLAY ENTIRE PHONEBOOK
// /////////////////////////////////////////////////////////////
void ReadPhonebook()
{
int lfdNR = 0;
do
{
// DISPLAY I
Serial.print("Data : ");
Serial.println(lfdNR,DEC);
// DISPLAY NAME
Serial.print("Name : ");
readName(lfdNR);
Serial.println(remoteName);
// DISPLAY PHONE NUMBER
Serial.print("Number : ");
readNumber(lfdNR);
Serial.println(remoteNumb);
lfdNR += 1;
Serial.println("--------------------------------");
readName(lfdNR);
}
while ( remoteName[0] != 0x20 ) ;
Serial.println("\nO.K.\n\n");
}
// /////////////////////////////////////////////////////////////
// SEND TEST SMS
// /////////////////////////////////////////////////////////////
void SendTestSMS()
{
for ( int lfdNR = 0 ; lfdNR < MaximumUsers; lfdNR++ )
{
readName(lfdNR); // remoteName
readNumber(lfdNR); // remoteNumb
if (remoteName[0] != 0x20)
{
// send the message
sms.beginSMS(remoteNumb);
sms.print(tstMsg);
sms.endSMS();
Serial.print(remoteNumb);Serial.println(" DONE.");
}
delay(499);
}
Serial.println("\nO.K.\n\n");
}
// /////////////////////////////////////////////////////////////
// SEND ALARM SMS
// /////////////////////////////////////////////////////////////
void SendAlarmSMS()
{
Serial.print("\nALARM @ T = ");
Serial.println(millis());
for ( int lfdNR = 0 ; lfdNR < MaximumUsers; lfdNR++ )
{
readName(lfdNR); // remoteName
readNumber(lfdNR); // remoteNumb
if (remoteName[0] != 0x20)
{
// send the message
sms.beginSMS(remoteNumb);
sms.print(txtMsg);
sms.endSMS();
Serial.print(remoteNumb);Serial.println(" SENT.");
}
delay(499);
}
Serial.println("\nO.K.\n\n");
}
// /////////////////////////////////////////////////////////////
// FORMAT ENTIRE PHONEBOOK
// /////////////////////////////////////////////////////////////
void formatEEPROM()
{
Serial.println("FORMATTING EEPROM.");
for (int j=0; j < (EEPROMSIZE + DatasetLength) ; j++)
{
eeaddress = j ;
Wire.beginTransmission(EEPROMADR);
Wire.write((int)(eeaddress >> 8)); // MSB
Wire.write((int)(eeaddress & 0xFF)); // LSB
Wire.write(0x20);
Wire.endTransmission();
delay(10);
}
Serial.println("\nO.K.\n\n");
}
// /////////////////////////////////////////////////////////////
// UPDATE LINEVOLTAGE STATUS
void UpDatePowerStatus()
{
float PGV = 5.0 * analogRead(A0) / 1023 ;
lcd.setCursor(0,3); lcd.print("# "); lcd.print(PGV,2);
lcd.setCursor(7,3);lcd.print(" [0-5] ");
StatusOLD = ALARM ;
if (PGV > 4.5) ALARM = true ;
if (PGV < 1.9) ALARM = false ;
if ((StatusOLD == true) && (ALARM == false )) Last_ALARM_off = millis() ;
if ((StatusOLD == false) && (ALARM == true )) Last_ALARM_on = millis() ;
digitalWrite(BEEP, ALARM);
if (ALARM == true) { lcd.setCursor(0,0); lcd.print("STATUS: OOOOPS "); }
else { lcd.setCursor(0,0); lcd.print("STATUS: NORMAL "); }
lcd.setCursor(0,1); lcd.write(byte(1)); lcd.setCursor(2,1);
lcd.print(Last_ALARM_on/1000);
lcd.setCursor(0,2); lcd.write(byte(2)); lcd.setCursor(2,2);
lcd.print(Last_ALARM_off/1000);
delay(10);
}
// /////////////////////////////////////////////////////////////
// UPDATE RSSI DISPLAY
void UpDateRSSI()
{
lcd.setCursor(0,2); lcd.write(byte(0)); // ANTENNA SYMBOL
lcd.setCursor(2,2); // lcd.print(scannerNetworks.getSignalStrength());
lcd.setCursor(7,2);lcd.print(" [0-31] ");
delay(10);
}
// /////////////////////////////////////////////////////////////
void TaskScheduler()
{
lcd.clear();
UpDatePowerStatus() ;
// UpDateRSSI();
if (ALARM)
{
// IS THE LAST ALARM OLDER THAN 60 MINUTES = 3600 SEC ???
if (( Last_SMS_out == 0) || (abs(millis()-Last_SMS_out)>3600000) )
{
SendAlarmSMS();
Last_SMS_out = millis();
}
}
if ( sms.available() )
{
Serial.println("Message received from:");
// Get remote number
sms.remoteNumber(remoteNumb, 20);
Serial.println(remoteNumb);
// An example of message disposal
// Any messages starting with # should be discarded
if (sms.peek() == '#') {
Serial.println("Discarded SMS");
sms.flush();
}
// Read message bytes and print them
while (char c = sms.read()) {
Serial.print(c);
}
Serial.println("\nEND OF MESSAGE");
// Delete message from modem memory
sms.flush();
Serial.println("MESSAGE DELETED");
}
}
// /////////////////////////////////////////////////////////////
void loop()
{
InitBuffy();
ReadUserInput();
int WhatsUp = Buffy[0] ;
UpDatePowerStatus();
switch (WhatsUp)
{
case 97 : // a - add entry
Serial.print("Add new Phonebook entry.\n");
addUSER();
print_options();
break;
case 98 : // b - beeper test
digitalWrite(BEEP, HIGH);
delay(999);
digitalWrite(BEEP, LOW);
break;
case 100 : // d - delete entry
Serial.print("Delete which entry [0 ... ");
Serial.print(MaximumUsers,DEC); Serial.print("] ???\n");
InitBuffy();
ReadUserInput();
DeleteWhat = Buffy[0] - 48 ;
aux = Buffy[1] - 48 ; if ((aux >= 0) && (aux <= 9))
{ DeleteWhat = DeleteWhat * 10 + aux ; }
aux = Buffy[2] - 48 ; if ((aux >= 0) && (aux <= 9))
{ DeleteWhat = DeleteWhat * 10 + aux ; }
aux = Buffy[3] - 48 ; if ((aux >= 0) && (aux <= 9))
{ DeleteWhat = DeleteWhat * 10 + aux ; }
if ( DeleteWhat < MaximumUsers ) deleteUSER( DeleteWhat );
else Serial.println("Oooops - that number was slightly too large.\n");
print_options();
break;
case 101 : // e - show eeprom
ReadEeprom();
print_options();
break;
case 102 : // f - format eeprom
formatEEPROM();
print_options();
break;
case 115 : // s - show phonebook
ReadPhonebook();
print_options();
break;
case 116 : // t - Test sms :-)
SendTestSMS();
print_options();
break;
default:
Serial.println("\nOOOPS - Invalid Input.\n");
print_options();
delay(10);
break;
}
delay(200);
}
// /////////////////////////////////////////////////////////////////////
// END OF FILE.
// /////////////////////////////////////////////////////////////////////
✈ Share your thoughts
The webmaster does not read these comments regularely. Urgent questions should be send via email.
Ads or links to completely uncorrelated things will be removed.
Your Browser says that you allow tracking. Mayst we suggest that you check that DNT thing ?