
В этом уроке мы дадим вторую жизнь телефонному аппарату с дисковым номеронабирателем, превратив его в мобильный (переносной) ретро-аппарат сотовой связи.
Есть стационарные телефонные аппараты у которых нет провода между трубкой и базой, но есть провод к розетке АТС. В нашем проекте у телефона останется провод между трубкой и базой, но пропадёт провод к розетке, так как связь будет осуществляться по сети GSM.
Описание работы ретро GSM телефона:
- После подачи питания дождитесь кратковременного колокольного звонка, он оповещает о том, что модем GSM зарегистрирован в сети сотового оператора связи.
- Телефон может принимать входящие звонки если трубка лежит на телефоне.
- Можно снять трубку (при отсутствии входящего звонка) для перехода в режим «я занят».
- Для ответа на входящий звонок нужно поднять трубку.
- Для завершения разговора нужно положить трубку на телефон.
- Набираемый номер должен состоять из 11 цифр и начинаться с цифры 7 или 8.
- Можно набирать номера 01, 02, 03, 04, 100, предварительно указав код города 8(495).
- Можно набирать номера 101, 102, 103, 104, 112.
- Ввод цифр номера осуществляется поворотом заводного диска до нужной цифры с последующим самовозвратом диска в исходное положение.
- Для совершения исходящего вызова выполните следующие действия:
- Поднимите трубку при отсутствии входящего вызова.
- Дождитесь появления гудка в трубке, он сигнализирует о наличии связи с сотовым оператором, следовательно, готовности к набору номера.
- Наберите номер вызываемого абонента.
- Если Вы ошиблись, опустите, поднимите трубку и повторите набор заново.
- Во время набора номера гудок в трубке должен пропасть. Вместо него, во время возврата заводного диска, имитируется звук "щелчков" от набора номера.
- После ввода последней (одиннадцатой) цифры номера дождитель появления сигнала вызова (длинные гудки или мелодия), или сигнала занят (короткие гудки).
- Если абонент ответит на Ваш вызов, то установится голосовое соединение.
- Если абонент разорвёт голосовую связь, Вы услышите сигнал занят (короткие гудки).
- Завершить вызов, набор, соединение или разговор можно в любое время повесив трубку.
- Динамик в телефонной трубке излучает звуки разговора и сигнал вызова (длинные гудки или мелодия), а все остальные сигналы формируются звукоизлучателем (зуммером), так же установленным в телефонную трубку.
Нам понадобится:
- Arduino / Piranha UNO.
- GSM/GPRS Shield A9.
- Источник питания 5В.
- Аккумулятор 18650 Li-ion, 3.7V
- Коннектор power jack Папа с клемником для Arduino
- Звук разговора и сигналов (можно выбрать один из вариантов):
- Динамик + микрофон + зуммер с генератором (в корпусе трубки телефона).
- Гарнитура с микрофоном + Trema зуммер с генератором.
- Колокольный звонок (можно выбрать один из вариантов):
- Колокольный звонок телефона + Trema силовой ключ + повышающий DC-DC преобразователь.
- Звонок из соленоида + колокол + Trema силовой ключ + повышающий DC-DC преобразователь.
- Звонок из сервопривода + колокол.
- Дисковый номеронабиратель (если его нет в корпусе телефона).
- Трема кнопка (если в корпусе телефона нет кнопки опускания трубки).
- Корпус телефона.
- Для реализации проекта нам необходимо установить библиотеку:
- iarduino_GSM.
- Библиотеки SoftwareSerial и Servo входят в стандартный набор Arduino IDE.
О том как устанавливать библиотеки, Вы можете ознакомиться на странице Wiki - Установка библиотек в Arduino IDE.
Схема сборки:
Arduino / Piranha UNO:
Если Вы собираетесь разместить устройство в корпусе телефона, найдите место для установки Arduino / Piranha UNO и закрепите её.

GSM/GPRS Shield:
Установите GSM/GPRS Shield A9 на Arduino / Piranha UNO:

На плате GSM/GPRS Shield A9 имеется слот для SIM-карт и переключатель UART. Установите SIM-карту в слот, выберите положение переключателя RX-7/TX-8.
Trema Shield:
На GSM/GPRS Shield A9 установите Trema Shield:

Номеронабиратель:
Подключите номеронабиратель к выводам GND, D5, D6 и Трема кнопку к выводу D4:

Если Вы собираете устройство в корпусе телефона, то вместо Trema кнопки подключите кнопку фиксации опускания трубки, между выводами GND и D4).
Колокольный звонок:
Подключите модули управления колокольным звонком:

Напряжение питания 5В подводится на вход повышающего DC-DC преобразователя, а напряжение с его выхода (уровень напряжения регулируется потенциометром преобразователя) подводится к соленоиду или родной катушке звонка телефона, через Trema силовой ключ, управление которым осуществляется через вывод D2.
Колокольный звонок можно собрать не на соленоиде, а на сервоприводе, подключив его к выводу D2:

Преимуществом данной схемы является меньшее число деталей. Но в скетче нужно присвоить переменной modeBEEL значение 2 (разкомментировать строку в начале скетча), а так же указать углы сервопривода при которых боёк касается колокола и удаляется от него (углы указываются в функции funcBELL в конце скетча).
Устройство ввода/вывода звука:
Если Вы собираетесь разместить динамик, микрофон и зуммер в трубке телефона, то подключите их согласно схеме: (трубка соединяется с аппаратом четырехпроводным кабелем).

В телефонной трубке, рядом с динамиком, необходимо разместить и зуммер. Он подключается к выводу D3 и нужен для подачи сигналов "готов к набору" (гудок при снятой трубке) и "занято" (прерывистые гудки после разрыва голосового соединения).
Если Вы не собираетесь размещать элементы ввода/вывода звука в телефонной трубке, то более простым вариантом является использование гарнитуры, которая подключается к соответствующему разъёму на плате GSM/GPRS Shield A9, а сигналы "готов к набору" и "занято" будут выводиться отдельно, через Trema зуммер, так же подключённый к выводу D3.

Если Вы не собираетесь размещать элементы ввода/вывода звука в телефонной трубке, но и не желаете использовать гарнитуру, то реализуйте вот такую схему:

Полная схема устройства:
Пример схемы с использованием гарнитуры и колокольного звонка на базе соленоида:

Подключение питания
Настройте выход 5-12V Источника питания 5В на максимум (12V) и установите в него Аккумулятор 18650 Li-ion, 3.7V. Подключите вывод 5-12V источника к джеку питания Arduino / Piranha UNO при помощи Коннектора power jack Папа с клемником.

Код программы (скетч):
В скетче предусмотрена возможность выбора типа колокольного звонка modeBEEL=0,1,2 и алгоритма работы кнопки фиксирующей опускание телефонной трубки flgHANG=0,1. Для выбора необходимого значения закомментируёте существующее и разкомментируйте требуемое.
В скетче используются библиотеки iarduino_GSM, SoftwareSerial и Servo (две последние входят в стандартный набор Arduino IDE).
Если Вы используете старую версию GSM шилда (A6), то в строках 10-12 необходимо заменить значения переменных pinPWR, pinRX и pinTX на соответсвтующие для этого шилда (pinPWR = 9, pinRX = 7, pinTX = 8).
// Телефонный аппарат с импульсным номеронабирателем на базе GSM/GPRS Shield A9
// ============================================================================
//
// Определяем номера выводов: //
uint8_t pinBELL = 2; // Вывод (выход) используемый для подключения силового ключа (для включения звонка вызова).
uint8_t pinBEEP = 3; // Вывод (выход) используемый для подключения излучателя звука (для вывода гудков в трубке).
uint8_t pinHANG = 4; // Вывод (вход) используемый для подключения кнопки (NC) фиксирующей опускание телефонной трубки.
uint8_t pinDIAL = 5; // Вывод (вход) используемый для подключения шунтирующего контакта (NO) номеронабирателя.
uint8_t pinPULSE = 6; // Вывод (вход) используемый для подключения тактирующего контакта (NC) номеронабирателя.
uint8_t pinPWR = 7; // Вывод PWR GSM шилда
uint8_t pinRX = 8; // Вывод (вход) используемый как линия RX (приём ) программной шины UART (данные от GSM/GPRS Shield к Arduino).
uint8_t pinTX = 9; // Вывод (выход) используемый как линия TX (передача) программной шины UART (данные к GSM/GPRS Shield от Arduino).
//
// Определяем тип колокольного звонка: //
uint8_t modeBEEL = 0; // Колокольный звонок собран на соленоиде (электромагнитной катушке, собственном звонке телефона).
// uint8_t modeBEEL = 1; // Колокольный звонок собран на электромоторе с бойком закреплённым к его ротору.
// uint8_t modeBEEL = 2; // Колокольный звонок собран на сервоприводе с бойком закреплённым к его качалке.
//
// Определяем алгоритм работы кнопки: //
bool flgHANG = 0; // 0-(NC) контакты кнопки замкнуты при лежащей трубке.
// bool flgHANG = 1; // 1-(NO) контакты кнопки разомкнуты при лежащей трубке.
//
// Объявляем переменные и функции: //
bool flgPowerON = true; // Определяем флаг подачи питания.
uint8_t cntPULSE; // Объявляем переменную для подсчёта импульсов в последней набранной цифре (10 импульсов для цифры 0).
uint8_t cntDigits; // Объявляем переменную для подсчёта набранных цифр номера.
char strNumber[12]; // Объявляем строку для хранения номера вызываемого/вызывающего телефона (11 символов номера + символ конца строки).
void funcBELL(bool); // Объявляем функцию управления звонком (true - подаёт один колокольный звон, false - выключает колокольный звонок).
//
// Подключаем библиотеки: //
#include <SoftwareSerial.h> // Подключаем библиотеку SoftwareSerial для программной реализации шины UART.
#include <iarduino_GSM.h> // Подключаем библиотеку iarduino_GSM для работы с GSM/GPRS Shield.
#include <Servo.h> // Подключаем библиотеку Servo для работы с сервоприводом (если колокольный звонок собран на сервоприводе).
//
// Создаём объекты: //
iarduino_GSM gsm(pinPWR); // Создаём объект gsm для работы с функциями и методами библиотеки iarduino_GSM, указав вывод PWR.
SoftwareSerial softSerial(pinRX, pinTX); // Создаём объект softSerial указывая выводы платы Arduino используемые в качестве линий RX и TX программной шины UART.
Servo srv; // Создаём объект srv для работы с функциями и методами библиотеки Servo (если колокольный звонок собран на сервоприводе).
//
void setup(){ //
Serial.begin(9600); // █ Инициируем связь с монитором последовательного порта на скорости 9600 бит/сек.
if(modeBEEL==2){srv.attach(pinBELL);} // Назначаем объекту srv управление сервоприводом подключённым к выводу pinBELL (если колокольный звонок собран на сервоприводе).
else{pinMode(pinBELL,OUTPUT); digitalWrite(pinBELL,LOW);} // Переводим вывод pinBELL в режим выхода и устанавливаем на нём уровень логического «0» (если колокольный звонок собран на соленоиде/электромагните/электромоторе).
pinMode(pinBEEP, OUTPUT); digitalWrite(pinBEEP, LOW ); // Переводим вывод pinBEEP в режим выхода и устанавливаем на нём уровень логического «0».
pinMode(pinHANG, INPUT ); digitalWrite(pinHANG, HIGH); // Переводим вывод pinHANG в режим входа и подтягиваем его к Vcc.
pinMode(pinDIAL, INPUT ); digitalWrite(pinDIAL, HIGH); // Переводим вывод pinDIAL в режим входа и подтягиваем его к Vcc.
pinMode(pinPULSE, INPUT ); digitalWrite(pinPULSE, HIGH); // Переводим вывод pinPULSE в режим входа и подтягиваем его к Vcc.
//
funcBELL(false); // Отключаем колокольный звонок входящего вызова.
gsm.begin(softSerial); // Инициируем работу GSM/GPRS Shield, указывая объект (или класс) для работы с её шиной UART.
//
// Ждём готовность GSM/GPRS Shield к работе: //
while(gsm.status()!=GSM_OK){delay(1000);} // Ждём завершения регистрации модема в сети оператора связи.
//
//
// Информируем о готовности модуля кратковременным включением колокольного звонка: //
if(flgPowerON){ //
// Если функция setup() выполняется в первый раз: //
uint32_t i = millis() + 1000; // Определяем длительность звонка готовности модуля.
while(i>millis()){ funcBELL(true); } funcBELL(false); // Включаем и отключаем колокольный звонок.
flgPowerON = false; // Сбрасываем флаг подачи питания.
} //
Serial.println(F("Готов к работе!")); // █ Можно добавить код выполняемый однократно после готовности аппарата при подаче питания.
} //
//
void loop (){ //
/******* СОВЕРШАЕМ ИСХОДЯЩИЙ ЗВОНОК *******/ // Для исходящего звонка нужно поднять трубку и набрать номер.
if(digitalRead(pinHANG)^flgHANG){ // Если на входе pinHANG установлена логическая «1» (трубка снята).
// Если трубка снята: //
delay(100); // Подавляем дребезг поднятия трубки.
// Готовимся к набору номера: //
cntDigits = 0; // Сбрасываем счетчик набранных цифр номера (номер ещё не набирался).
strNumber[0]='\0'; // Чистим строку набираемого номера.
digitalWrite(pinBEEP, LOW); // Отключаем тоновый сигнал в трубке телефона (если он был включён).
Serial.println(F("Трубка снята, проверяем готовность к набору номера ...")); // █ Можно добавить код выполняемый однократно при поднятии трубки для набора номера, до проверки наличия связи с оператором.
// Проверяем готовность GSM/GPRS Shield к работе: //
if(gsm.status()!=GSM_OK){ //
// Если модуль не готов к работе (например, ошибка регистрации в сети): //
Serial.println(F("Перезагрузка модуля")); // █ Выводим сообщение о перезагрузке модуля.
// Заново инициируем работу с модулем: //
setup(); //
} //
// Информируем о готовности к набору номера: //
digitalWrite(pinBEEP, HIGH); // Включаем тоновый сигнал в трубке телефона (оповещая о готовности к набору номера).
Serial.println(F("Можно набирать номер ...")); // █ Можно добавить код выполняемый однократно при поднятии трубки для набора номера, после проверки связи с оператором.
while(digitalRead(pinHANG)^flgHANG){ // Входим в цикл, который будет завершён опусканием трубки на телефон.
// Цикл выполняется всё время, пока снята трубка: //
if(!digitalRead(pinDIAL)){ // Если шунтирующая контактная группа номеронабирателя замкнулась (значат набор цифры), то ...
// Если начинается набор очередной цифры номера: //
delay(20); // Подавляем дребезг шунтирующей контактной группы номеронабирателя.
digitalWrite(pinBEEP, LOW); // Отключаем тоновый сигнал в трубке телефона (если он был включён).
cntPULSE=0; // Сбрасываем счётчик поступивших импульсов от номеронабирателя.
Serial.print(F("Набирается цифра ... ")); // █ Можно добавить код выполняемый однократно перед набором каждой цифры номера
while(!digitalRead(pinDIAL) && (digitalRead(pinHANG)^flgHANG)){ // Если чтение импульсов набираемой цифры разрешено (шунтирующие контакты номеронабирателя замкнуты) и трубка снята, то ...
// Цикл выполняется пока набирается очередная цифра номера: //
if(digitalRead(pinPULSE)){ // Если поступил тактирующий импульс (импульсная контактная группа номеронабирателя разомкнулась), то ...
// Фронт импульса: //
digitalWrite(pinBEEP, HIGH); // Включаем тоновый сигнал в трубке телефона.
delay(5); // Подавляем дребезг импульсной контактной группы номеронабирателя.
digitalWrite(pinBEEP, LOW); // Отключаем тоновый сигнал в трубке телефона.
while(digitalRead(pinPULSE) && (digitalRead(pinHANG)^flgHANG)){delay(5);} // Ждём завершения тактирующего импульса (замыкания импульсной контактной группы номеронабирателя) или опускания трубки.
// Спад импульса: //
delay(5); // Подавляем дребезг импульскной контактной группы номеронабирателя.
cntPULSE++; // Увеличиваем счётчик полученных импульсов.
} //
} //
delay(20); // Подавляем дребезг шунтирующей контактной группы номеронабирателя.
// Очередная цифра номера набрана: //
if(cntPULSE){ // Если от импульсной контактной группы номеронабирателя поступил хотя бы 1 импульс, то ...
// Если цифра набрана корректно (во время набора поступил хотя бы один импульс) //
if(cntPULSE>=10){cntPULSE=0;} // Если поступило 10 импульсов, значит набрана цифра 0.
strNumber[cntDigits]=cntPULSE+48; // Сохраняем код набранной цифры в строку с набираемым номером.
cntDigits++; // Переходим к следующей цифре набираемого номера.
strNumber[cntDigits]='\0'; // Сохраняем код конца строки.
Serial.println(cntPULSE); // █ Можно добавить код выполняемый однократно после набора каждой цифры номера.
} //
// Проверяем введённые цифры номера: //
if( cntDigits==11 // Если набрано 11 цифр номера *(***)***-**-** - обычный номер.
|| (cntDigits==7 && strncmp("8495100", strNumber, 8)==0) // Если набрано 7 цифр номера 8(495)100 - точное время (городской).
|| (cntDigits==6 && strncmp("849501", strNumber, 7)==0) // Если набрано 6 цифр номера 8(495)01 - пожарная служба (городской).
|| (cntDigits==6 && strncmp("849502", strNumber, 7)==0) // Если набрано 6 цифр номера 8(495)02 - полиция (городской).
|| (cntDigits==6 && strncmp("849503", strNumber, 7)==0) // Если набрано 6 цифр номера 8(495)03 - скорая помощь (городской).
|| (cntDigits==6 && strncmp("849504", strNumber, 7)==0) // Если набрано 6 цифр номера 8(495)04 - газовая служба (городской).
|| (cntDigits==3 && strncmp("101", strNumber, 4)==0) // Если набрано 3 цифры номера 101 - пожарная служба.
|| (cntDigits==3 && strncmp("102", strNumber, 4)==0) // Если набрано 3 цифры номера 102 - полиция.
|| (cntDigits==3 && strncmp("103", strNumber, 4)==0) // Если набрано 3 цифры номера 103 - скорая помощь.
|| (cntDigits==3 && strncmp("104", strNumber, 4)==0) // Если набрано 3 цифры номера 104 - газовая служба.
|| (cntDigits==3 && strncmp("112", strNumber, 4)==0) // Если набрано 3 цифры номера 112 - экстренные оперативные службы.
){ //
// Если номер набран полностью, то инициируем вызов ... //
if(gsm.CALLdial(strNumber)){ // Инициируем исходящий голосовой вызов на номер указанный в строке strNumber.
// Если исходящий вызов инициирован, ждём завершения набора номера ... //
Serial.println((String) "Набор номера " + strNumber + " ..."); // █ Можно добавить код выполняемый однократно при начале набора номера.
while(gsm.CALLstatus()==GSM_CALL_OUT_DIAL && (digitalRead(pinHANG)^flgHANG)){} // Цикл выполняется пока установлено состояние вызова "набирается номер" и снята трубка.
while(gsm.CALLstatus()==GSM_CALL_OUT_DIAL && (digitalRead(pinHANG)^flgHANG)){} // Повторяем цикл на случай кратковременного изменения статуса вызова.
while(gsm.CALLstatus()==GSM_CALL_OUT_DIAL && (digitalRead(pinHANG)^flgHANG)){} // Повторяем цикл на случай кратковременного изменения статуса вызова.
if(gsm.CALLstatus()==GSM_OK){ //
// Если произошёл обрыв связи с оператором: //
Serial.println(F("произошёл обрыв связи с оператором.")); // █ Можно добавить код выполняемый однократно при обрыве связи с оператором.
} //
if(gsm.CALLstatus()==GSM_CALL_OUT_BEEP){ // Если установилось состояние вызова "дозвон", то ...
// Если начался дозвон, то ждём пока вызываемый абонент не ответит ... //
Serial.println(F("Ожидание ответа ...")); // █ Можно добавить код выполняемый однократно при поступлении гудков у вызываемого абонента.
while(gsm.CALLstatus()==GSM_CALL_OUT_BEEP && (digitalRead(pinHANG)^flgHANG)){} // Цикл выполняется пока установлено состояние вызова "дозвон" и снята трубка.
delay(500); // Даём время для установки состояния вызова - "соединён".
} //
if(gsm.CALLstatus()==GSM_CALL_ACTIVE){ // Если установилось состояние вызова "соединён", то ...
// Если установлено активное голосовое соединение ... //
Serial.println(F("Исходящее голосовое соединение установлено.")); // █ Можно добавить код выполняемый однократно при установлении активного голосового соединения.
while(gsm.CALLstatus()==GSM_CALL_ACTIVE && (digitalRead(pinHANG)^flgHANG)){} // Цикл выполняется пока установлено активное голосовое соединение и снята трубка.
// Если голосовое соединение разорвано или его требуется разорвать ... //
} //
Serial.println(F("Разговор завершён.")); // █ Можно добавить код выполняемый однократно в момент завершения разговора.
} //
// Разрываем голосовое соединение, если разговор завершён опусканием трубки: //
gsm.CALLend(); // Разъединяем голосовое соединение.
// Выводим короткие звуковые сигналы в трубку телефона... //
while(digitalRead(pinHANG)^flgHANG){ // Цикл выполняется пока снята трубка.
if(millis()%1000<500){digitalWrite(pinBEEP, HIGH);} // Выводим тоновый сигнал в трубке телефона в течении первых 500 мс каждых 1000 мс.
else {digitalWrite(pinBEEP, LOW );} // Отключаем тоновый сигнал в трубке телефона в течении остального времени из 1000 мс.
} digitalWrite(pinBEEP, LOW ); // Отключаем тоновый сигнал в трубке телефона.
} //
} //
gsm.CALLend(); // Разъединяем голосовое соединение, если нам позвонили пока поднята трубка (до или в момент набора номера).
} //
Serial.println(F("Трубка опущена на аппарат.")); // █ Можно добавить код выполняемый однократно в момент опускания трубки на аппарат.
}else{ //
/******* ПРИНИМАЕМ ВХОДЯЩИЙ ЗВОНОК *******/ // Для приёма входящих звонков трубка должна быть опущена.
// Если трубка лежит на телефоне: //
delay(100); // Подавляем дребезг опускания трубки.
digitalWrite(pinBEEP, LOW); // Отключаем тоновый сигнал в трубке телефона (если он был включён).
Serial.println(F("Трубка лежит на аппарате, режим ожидания звонка ...")); // █ Можно добавить код выполняемый однократно в момент перехода в режим ожидания входящего звонка
while(!digitalRead(pinHANG)^flgHANG){ // Входим в цикл, который будет завершён поднятием трубки с телефона.
// Цикл выполняется всё время, пока трубка не поднята: //
if(gsm.CALLavailable(strNumber)){ // Функция CALLavailable() возвращает true если есть входящий дозванивающийся вызов, номер вызывающего абонента сохраняется в строку strNumber.
// Если есть входящий вызов в режиме дозвона, то ждём ответа поднятием трубки ... //
Serial.println((String)"Входящий вызов "+strNumber+", ждём поднятия трубки ..."); // █ Можно добавить код выполняемый однократно в момент поступления входящего звонка
while(gsm.CALLavailable() && !(digitalRead(pinHANG)^flgHANG)){ // Цикл выполняется пока есть входящий вызов в режиме дозвона и трубка не поднята.
// Информируем колокольными звонками о наличии входящего вызова: //
while(millis()%4000<2000){funcBELL(true);} // Включаем колокольный звонок в течении первых 2000 мс каждых 4000 мс.
funcBELL(false); // Отключаем колокольный звонок в течении остального времени.
} //
delay(100); // Подавляем дребезг поднятия трубки.
// Проверяем почему был завершён цикл ожидания ответа ... //
if(digitalRead(pinHANG)^flgHANG){ // Если трубка снята.
// Если цикл завершён по причине поднятия трубки: //
Serial.println(F("Трубка снята, отвечаем на звонок")); // █ Можно добавить код выполняемый однократно в момент поднятия трубки для ответа на входящий звонок.
if(gsm.CALLavailable()){ // Функция CALLavailable() возвращает true если есть входящий дозванивающийся вызов.
// Если вызывающий абонент всё ещё ждёт ответа (поднятия трубки) ... //
gsm.CALLup(); // Отвечаем на вызов.
// Ждём пока состояние вызова "дозвон" не сменится ... //
while(gsm.CALLstatus()==GSM_CALL_IN_BEEP){;} // Функция CALLstatus() возвращает статус текущего голосового вызова, значение GSM_CALL_IN_BEEP указывает на наличие входящего дозванивающегося вызова.
if(gsm.CALLstatus()==GSM_CALL_ACTIVE){ // Функция CALLstatus() возвращает статус текущего голосового вызова, значение GSM_CALL_ACTIVE указывает на наличие активного голосового соединения.
// Если установлено активное голосовое соединение ... //
Serial.println(F("Входящее голосовое соединение установлено.")); // █ Можно добавить код выполняемый однократно при установлении активного голосового соединения.
while(gsm.CALLstatus()==GSM_CALL_ACTIVE && (digitalRead(pinHANG)^flgHANG)){} // Цикл выполняется пока установлено активное голосовое соединение и снята трубка.
} //
// Если голосовое соединение разорвано или требуется разорвать ... //
Serial.println(F("Разговор завершён.")); // █ Можно добавить код выполняемый однократно в момент завершения разговора.
// Разрываем голосовое соединение, если разговор завершён опусканием трубки: //
gsm.CALLend(); // Разъединяем голосовое соединение, это требуется если мы инициировали разрыв соединения опусканием трубки.
} //
// Выводим короткие звуковые сигналы в трубку телефона... //
while(digitalRead(pinHANG)^flgHANG){ // Цикл выполняется пока снята трубка.
if(millis()%1000<500){digitalWrite(pinBEEP, HIGH);} // Выводим тоновый сигнал в трубке телефона в течении первых 500 мс каждых 1000 мс.
else {digitalWrite(pinBEEP, LOW );} // Отключаем тоновый сигнал в трубке телефона в течении остального времени из 1000 мс.
} digitalWrite(pinBEEP, LOW ); // Отключаем тоновый сигнал в трубке телефона.
}else{ //
// Если цикл завершён по причине сброса вызова: //
Serial.println(F("Вызов завершён по причине сброса вызова")); // █ Можно добавить код выполняемый однократно в момент сброва вызова.
} //
}else{ //
// Если входящих вызовов в режиме дозвона нет: //
if(gsm.status()!=GSM_OK){ //
// Если модуль не готов к работе (например, ошибка регистрации в сети): //
Serial.println(F("Перезагрузка модуля")); // █ Выводим сообщение о перезагрузке модуля.
// Заново инициируем работу с модулем: //
setup(); //
} //
} //
} //
} //
} //
//
// Функция управления колокольным звонком: // В зависимости от параметра (f) функция либо отключает колокольный звонок, либо подаёт один колокольный звон входящего вызова.
void funcBELL(bool f){ // В данной функции можно регулировать тональность колокольного звонка, меняя задержку delay().
if(modeBEEL==0){ //
// Если колокольный звонок собран на соленоиде (электромагнитной катушке): //
if(f){digitalWrite(pinBELL, HIGH); delay(20); // Если установлен флаг f, то: - подаём высокий уровень на выход pinBELL (силовой ключ замкнётся , через катушку потечёт ток и боёк ударит о колокол), ждём 20 мс.
digitalWrite(pinBELL, LOW ); delay(20); // - подаём низкий уровень на выход pinBELL (силовой ключ разомкнётся, катушка будет обесточена и боёк удалится от колокола), ждём 20 мс.
}else{digitalWrite(pinBELL, LOW );} // Если сброшен флаг f, то - подаём низкий уровень на выход pinBELL (силовой ключ разомкнётся, катушка будет обесточена и боёк удалится от колокола).
}else if(modeBEEL==1){ //
// Если колокольный звонок собран на электромоторе: //
if(f){digitalWrite(pinBELL, HIGH);} // Если установлен флаг f, то - подаём высокий уровень на выход pinBELL (силовой ключ замкнётся , электромотор включится и боёк на его роторе начнёт бить по колоколу).
else {digitalWrite(pinBELL, LOW );} // Если сброшен флаг f, то - подаём низкий уровень на выход pinBELL (силовой ключ разомкнётся, электромотор отключится и боёк перестанет бить по колоколу).
}else if(modeBEEL==2){ //
// Если колокольный звонок собран на сервоприводе: //
if(f){srv.write(50); delay(20); // Если установлен флаг f, то: - поворачиваем сервопривод на угол при котором боёк закреплённый к его качалке ударит о колокол, ждём 20 мс.
srv.write(60); delay(20); // - поворачиваем сервопривод на угол при котором боёк закреплённый к его качалке удалится от колокола, ждём 20 мс.
}else{srv.write(60);} // Если сброшен флаг f, то - поворачиваем сервопривод на угол при котором боёк закреплённый к его качалке удалится от колокола.
} // Вместо углов 50° и 60° необходимо указать Ваши углы (значение подбирается экспериментально).
}
В разделе функции funcBELL отвечающем за управление сервоприводом указаны углы 50° и 60° (три последние строки). Если Вы используете колокольный звонок на сервоприводе, измените угол 50° на тот при котором сервопривод ударяет бойком по колоколу, а угол 60° на тот при котором боёк удаляется от него.
Алгоритм работы скетча:
До кода Setup() определяются номера выводов, тип колокольного звонка, алгоритм работы кнопки фиксирующей опускание трубки, объявляются переменные и функции, подключаются библиотеки, и создаются объекты для работы с ними.
В коде setup() конфигурируются выбранные выводы, инициируется работа GSM/GPRS Shield, звук переводится на разъём гарнитуры, выполняется цикл ожидания готовности GSM/GPRS Shield к работе (регистрация в сети оператора). И после выполнения всех указанных действий выполняется оповещение о готовности к работе, путем включения колокольного звонка на 1 секунду.
Код цикла loop() разделён на две основные части: совершение исходящих звонков (данная часть выполняется если телефонная трубка снята) и приём входящих вызовов (данная часть выполняется если трубка лежит на телефоне).
Совершение исходящих звонков состоит из следующих действий:
- Обнуление переменных, проверка связи с оператором, вывод сигнала в трубку телефона о готовности к работе.
- Дальнейшие действия происходят в отдельном цикле, выход из которого возможен только если положить трубку на телефон. Так же в этом цикле постоянно сбрасывается голосовое соединение, не давая другим абонентам позвонить нам при снятой трубке.
- Если начинается набор номера, то отключаем сигнал в телефонной трубке, подсчитываем количество импульсов набираемых цифр. После каждой введённой цифры из значение добавляется в строку с номером, которая проверяется на корректность (достижение 11 знаков, или совпадение с коротким номером). При желании Вы можете добавить или удалить некоторые короткие номера из скетча.
- Если номер в строке корректен, то производится его набор, с дальнейшей проверкой состояния связи. Если связь установлена, то выполняется пустой цикл ожидания её разрыва.
- При разрыве голосового соединения выполняется цикл вывода коротких звуковых сигналов в телефонную трубку. Этот цикл выполняется постоянно, пока не положить трубку на телефон.
Приём входящих вызовов состоит из следующих действий:
- Отключение тонального сигнала в телефонной трубке (на случай если он был).
- Дальнейшие действия происходят в отдельном цикле, выход из которого возможен только если снять трубку с телефона при отсутствии входящего вызова.
- Если зафиксирован входящий вызов, то выполняется цикл включающий колокольный звонок, пока входящий вызов не изменит свой статус или не будет поднята трубка.
- Если статус вызова изменился по причине поднятия трубки, то устанавливается голосовое соединение, и выполняется пустой цикл ожидания разрыва этого соединения.
- После разрыва голосового соединения выполняется цикл вывода коротких звуковых сигналов в телефонную трубку. Этот цикл выполняется постоянно, пока не положить трубку на телефон.
В конце скетча определена функция управления колокольным звонком. Данная функция принимает один параметр типа bool (true - ударить в колокол и освободить его / false - освободить колокол). В зависимости от значения переменной modeBEEL функция работает с колокольными звонками собранными на базе соленоида, родного звонка телефона, сервопривода или электромотора.

Обсуждение