КОРЗИНА
магазина
8 (499) 500-14-56 | ПН. - ПТ. 12:00-18:00
ЛЕСНОРЯДСКИЙ ПЕРЕУЛОК, 18С2, БЦ "ДМ-ПРЕСС"

Ретро GSM телефон на Arduino 1958 года, с дисковым номеронабирателем

В этом уроке мы дадим вторую жизнь телефонному аппарату с дисковым номеронабирателем, превратив его в мобильный (переносной) ретро-аппарат сотовой связи.

Есть стационарные телефонные аппараты у которых нет провода между трубкой и базой, но есть провод к розетке АТС. В нашем проекте у телефона останется провод между трубкой и базой, но пропадёт провод к розетке, так как связь будет осуществляться по сети GSM.

Описание работы ретро GSM телефона:

  • После подачи питания дождитесь кратковременного колокольного звонка, он оповещает о том, что модем GSM зарегистрирован в сети сотового оператора связи.
  • Телефон может принимать входящие звонки если трубка лежит на телефоне.
  • Можно снять трубку (при отсутствии входящего звонка) для перехода в режим «я занят».
  • Для ответа на входящий звонок нужно поднять трубку.
  • Для завершения разговора нужно положить трубку на телефон.
  • Набираемый номер должен состоять из 11 цифр и начинаться с цифры 7 или 8.
  • Можно набирать номера 01, 02, 03, 04, 100, предварительно указав код города 8(495).
  • Можно набирать номера 101, 102, 103, 104, 112.
  • Ввод цифр номера осуществляется поворотом заводного диска до нужной цифры с последующим самовозвратом диска в исходное положение.
  • Для совершения исходящего вызова выполните следующие действия:
    • Поднимите трубку при отсутствии входящего вызова.
    • Дождитесь появления гудка в трубке, он сигнализирует о наличии связи с сотовым оператором, следовательно, готовности к набору номера.
    • Наберите номер вызываемого абонента.
    • Если Вы ошиблись, опустите, поднимите трубку и повторите набор заново.
    • Во время набора номера гудок в трубке должен пропасть. Вместо него, во время возврата заводного диска, имитируется звук "щелчков" от набора номера.
    • После ввода последней (одиннадцатой) цифры номера дождитель появления сигнала вызова (длинные гудки или мелодия), или сигнала занят (короткие гудки).
    • Если абонент ответит на Ваш вызов, то установится голосовое соединение.
    • Если абонент разорвёт голосовую связь, Вы услышите сигнал занят (короткие гудки).
  • Завершить вызов, набор, соединение или разговор можно в любое время повесив трубку.
  • Динамик в телефонной трубке излучает звуки разговора и сигнал вызова (длинные гудки или мелодия), а все остальные сигналы формируются звукоизлучателем (зуммером), так же установленным в телефонную трубку.

Нам понадобится:

    Для реализации проекта нам необходимо установить библиотеку:
  • 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 функция работает с колокольными звонками собранными на базе соленоида, родного звонка телефона, сервопривода или электромотора.

Ссылки:




Обсуждение

Гарантии и возврат Используя сайт Вы соглашаетесь с условями