Установка Asterisk IP-PBX в качестве домашней АТС для начинающих.

15th Октябрь 2010 | Метки: , , , , , , , , ,
Опубликовал: Andrey [rtty] Shidakov

Введение.
Решил я построить дома собственную АТС. Выбор пал на программную АТС Asterisk —
свободное решение компьютерной телефонии с открытым исходным кодом от компании Digium. Приложение работает на операционных системах GNU/Linux, FreeBSD и Solaris. В качестве ОС была выбрана уже установленная на home-server FreeBSD 8.1.
Информации достаточно много по этому вопросу, тут я попытался собрать все необходимое для настройки простенькой домашней АТС. Статья расчитана на новичков, поэтому экспертам врядли будет интересна.

Итак, что же умеет Asterisk?
Asterisk, в комплексе с необходимым оборудованием, обладает всеми возможностями классической АТС, поддерживает множество VoIP протоколов и предоставляет функции голосовой почты, конференций, интерактивного голосового меню (IVR), центра обработки вызовов (постановка звонков в очередь и распределение их по агентам используя различные алгоритмы), запись CDR и прочие функции. Для создания собственной функциональности можно воспользоваться собственным языком Asterisk для написания диалплана, написав модуль на языке C, либо воспользовавшись AGI, который является гибким и универсальным интерфейсом для интеграции с внешними системами обработки данных, выполняемое через AGI может быть написано на любом языке программирования.

Проектируем будущую АТС.
Для начала накидаем схему будущей телефонной сети и определимся что и как будет работать:

И так, ядро системы Asterisk, VOIP шлюз Linksys SPA3102 для подключения к VoIP городской телефонной линии и обычного телефонного аппарата, GSM шлюз (в качестве его используется 3G модем Huawey E1550), различные IP телефоны (программные, например X-Lite, PhonerLite и различные аппаратные).

Итак, задачи:
— При поступлении входящего вызова на городскую линию/gsm/voip в дневное время звонят все телефоны, если в течении минуты ответа нет — включается голосовая почта. В ночное время включается голосовое меню, которое позволяет либо оставить сообщение либо ввести пин-код (известный тем людям, кому можно звонить в ночное время) и сделать вызов.
— Возможность переводить вызов (во время разговора нажатием #(внутренний номер абонента)#).
— Внутренняя связь по коротким 2-х значным номерам (у меня 10-19)
— Прием факсов и отправка на email. (в процессе)
— Отправка голосовой почти на email. (в процессе)

Оборудование:
Сам Asterisk я установил на домашний сервер на базе Intel Atom, ОС FreeBSD 8.1. В качестве VoIP шлюза был выбран Linksys SPA3102 (у нас в Тамбове его можно приобрести в http://torg68.ru/ , я покупал за 2824 руб, доставка около недели, самовывоз с Чичканова 7) . К его настройкам мы вернемся чуть позже. Для связи с сотовой сетью был установлен Huawei E1550 (стоимостью 590 рулей на момент написания статьи).  IP телефоны — любые, обычный телефон — любой с тональным набором, АОН должен поддерживать стандарт FSK-CallerID, «Русский-АОН» не подойдет.

Устанавливаем Asterisk
Установку производим из портов:

cd /usr/ports/net/asterisk16
make install clean

echo 'asterisk_enable="YES"' >> /etc/rc.conf
/usr/local/etc/rc.d/asterisk start

Для подключения к консоли: astrisk -rvvvvvv (количество ‘v’ повышает уровень вывода)
Далее приведу свои конфигурационные файлы:
/usr/local/etc/asterisk/sip.conf

[general]
; нужно, чтобы принимать входящие вызовы через SIPNET
register=>имя_пользователя:пароль@sipnet.ru/идентификатор  

[HOME-USERS](!)
type=friend ; может как принимать, так и совершать звонки
host=dynamic
nat=no
canreinvite=no
context=from-home
disallow=all ; разрешить только определенные кодэки
allow=ulaw ; данная конфигурация лучше подходит
allow=alaw ; для локальной сети - без сжатия.
monitor=yes
language=ru ; язык
dtmfmode=rfc2833

[spa3102] ; для входящих с городской линии через шлюз Linksys spa3102
type=friend
host=dynamic
secret=пароль
context=from-gw

[10](HOME-USERS) ;  Linksys spa3102
username=linksys
secret=пароль

[SIPNET] ; для sipnet.ru (для приема входящих директива register в начале файла)
secret = пароль
defaultuser = имя_пользователя
trunkname = sipnet
callerid =
hasexten = no
hassip = yes
hasiax = no
host = sipnet.ru
context = from-sipnet ; контекст с таким именем должен существовать в dialplan’е
insecure = invite
fromuser = 0012345678 ; ваш id в SIPNET
fromdomain = sipnet.ru
type = peer
disallow = all
allow = alaw
allow = ulaw
allow = g729
nat = yes
canreinvite = no
dtmfmode = auto

[11](HOME-USERS) ; IP - телефон, например программа X-Lite
username=user1
secret=pass1
callerid="user1" <11>

[12](HOME-USERS) ; Другой IP - Телефон
username=user2
secret=pass2
callerid="user2" <12>

/usr/local/etc/asterisk/extensions.ael:

// Описываем конетксты согласно своей схеме. В моем случае схема такая:
// 1. from-home - исходящие звонки с домашних телефонов, которые подразделяются на более точные контексты
//    1.1 local-users - домашние номера 2-х значные (пользователи моей АТС)
//    1.2 city-numbers - городские 6-ти значные номера (через шлюз gw (в моем случае Linksys SPA3102-EU))
//    1.3 mobile-numbers - 11 значные мобильные (Тамбов) (через шлюз gsm (в моем случаем 3G модем Huaway))
//    1.4 mezhgorod-numbers - междугородние (через sipnet (сеть интернет-телефонии))
// 2. from-gw входящие звонки на gw
// 3. datacard-incoming входящие звонки на gsm
// 4. from-sipnet входящие звонки на sipnet

globals {
        ALL=SIP/10&SIP/11&SIP/12;
}

context from-home {
        includes {
                local-users;
                mobile-numbers;
                mezhgorod-numbers;
                city-numbers;
        };
};

// Пользователи моей АТС с 2-х значными номерами
context local-users {
        _500 => {                             // Чтобы записывать голосовые файлы для меню
            Wait(2);
            Record(/tmp/ast:gsm);
            Wait(2);
            Playback(/tmp/ast);
            Wait(2);
            Hangup;
        };

        _20 => VoiceMailMain(${CALLERID(NUM)}); // Набрав 20 - попадаем в голосовую почту

        _XX => Dial(SIP/${EXTEN},60,tT);  // Для внутренних звонков

};

// Звонки на городские номера будут адресоваться по обычной телефонной линии через Linksys
context city-numbers {
        _XXXXXX => Dial(SIP/${EXTEN}@spa3102,,T);
};

// Звонки на мобильные номера будут адресоваться через gsm шлюз
context mobile-numbers {
        _89XXXXXXXXX => Dial(Datacard/datacard0/${EXTEN},,T);
};

// Звоник на межгород через sipnet
context mezhgorod-numbers {
        _849XXXXXXXX => Dial(SIP/${EXTEN}@SIPNET,,T);
};

// Входящие с SIPNET
context from-sipnet {
    s =>
    {
        GotoIfTime(8:00-22:00|*|*|*?inc-day,s,1); // В дневное время inc-day
        Goto(inc-night,s,1);                      // В ночное время inc-night;
    };
}

// Входящие с GSM-GW
context datacard-incoming {
    s =>
    {
        GotoIfTime(8:00-22:00|*|*|*?inc-day,s,1); // В дневное время inc-day
        Goto(inc-night,s,1);                      // В ночное время inc-night;
    };
}

// Входящие с телефонной линии через Linksys
context from-gw {
    10 =>
    {
        NoOp(CALLERID=${CALLERID(all)}); // Используется для отладки и вывода в консоль
//      GotoIfTime(8:00-22:00|*|*|*?inc-day,s,1); // В дневное время inc-day
//      Goto(inc-night,s,1);
        Goto(inc-day,s,1);                      // В ночное время inc-night;
    };
}

context inc-day {
    s => {
    SET(CHANNEL(language)=ru);
    Dial(${ALL},60,t); // Звоним на телефоны (ALL обозначено в начале файла в секции GLOBAL)
        if ("${DIALSTATUS}" = "CHANUNAVAIL") { // Если абонент не доступен
                Wait(2);                                     // Ждем 2 секунды
                Voicemail(20,u);                           // Включаем голосовую почту с сообщением "Не доступен"
                Hangup;                                      // Вешаем трубку
                };
        if ("${DIALSTATUS}" = "BUSY") {             // Если занято
                Hangup;                                      // Вешаем трубку
                };
        if ("${DIALSTATUS}" = "NOANSWER") {     // Если никто не ответит в течение 60 секунд (задается вторым параметром в Dial
                Wait(2);
                Voicemail(20);
                };
    };
}

context inc-night {           // Вызывается ночью
    s =>
    {
      Set(TIMEOUT(digit)=5);
      Set(TIMEOUT(response)=15);
      Answer();
      Background(/usr/local/share/asterisk/voice/night-hi); // Голос: Что нибудь типа этого - К сожалению в ночное время входящие
            //вызовы запрещены, нажмите 1 чтобы оставить сообщение, или 2 чтобы ввести пароль для совершения вызова.
    vvod:
      WaitExten; // Ждем ввод
    };
    _1 => Voicemail(20); // Нажато 1 - вызываем почту
    _2 => Goto(pass,s,1); // Нажато 2 - переходим в контекст проверки пин кода
    _i => { // Не правильный ввод
    Background(/usr/local/share/asterisk/voice/never); // Проигрываем запись о неверном вводе
    goto s|vvod; //Не правильный ввод
    }
    # => Hangup; // # - повесить трубку
}

context pass {
    s =>
    {
      Background(/usr/local/share/asterisk/voice/pass); // Голос: Введите пароль
    vvod:
      WaitExten; // Ждем ввода
    };
    _321* => Dial(${ALL},60,t); // 321* - Пароль. Если пароль верный, пропускаем вызов.
    * => {
        Background(/usr/local/share/asterisk/voice/never); // Неправильный ввод
        goto s|vvod;                                                   // Не правильный ввод
    };
    i => { // Неверный ввод
        goto s|vvod;
    };
}

/usr/local/etc/asterisk/features.conf:
В секции [featuremap] должны быть следующие строки (это нужно для возможности перевода звонка):

blindxfer => * ; Blind transfer (default is #)
disconnect => 00 ; Disconnect (default is *)
atxfer => # ; Attended transfer

Чтобы работал перевод звонка, в диалплане у команды Dial есть специальные опции — tT:
T — разрешает звонящему пользователю перевести звонок (нажатием #внутренний_номер#)
t — разрешает принимающему звонок пользователю перевести звонок

В /usr/local/etc/asterisk/voicemail.conf в секции [default] дописываем пользователей:

10 => pass, User, user@mail-domain.ru ; ID - 10, пароль - pass, мыло - user@mail-domain.ru (отправляется уведомление)

/usr/local/etc/asterisk/datacard.conf — описан ниже в разделе по настройке GSM-шлюза.
Остальные конфиги оставляем без изменений.

Настраиваем GSM-шлюз:
В качестве GSM шлюза подойдут Huawei K3715, Huawei E169 / K3520, Huawei E1550. Следующие модемы НЕ будут работать: Huawei E160 / K3565. Был куплен Huawei E1550 за 550 руб (МТС). Голосовые функции были активны. Модем был залочен на оператора. Чтобы разлочить модем, заходим на сайт http://a-zgsm.com/huawei.php вводим в поле IMEI своего модема (обычно написан на корпусе) и получаем два кода — unlock и flash коды. У Huawei e1550 достаточно вставить другую симку и в самой программе ввести unlock код, если планируете использовать в будущем модем по назначению, то желательно прошить универсальным Dashboard с поддержкой всех операторов.  Flash код нужен для разлочки некоторых других модемов, например e160g, в него сначала зашивается Dashboard, затем прошивка, при прошивке будет запрошен Falsh код. Более подробно по вопросу разлочки, а также скачать Dashboardы и прошивки можно тут и тут.
Подключаем и настраиваем модем на  FreeBSD:
Для того чтобы модем определился, нужно собрать и установить модуль u3g (прим. проверенно в FreeBSD 8.1, в более ранних версиях были проблемы).

cd /sys/modules/usb/u3g
make && make install && make clean

Подключаем модем и наблюдаем:

ugen0.2:  at usbus0
u3g0:  on usbus0
u3g0: Found 3 ports.

Видим, появились порты:

[dron@nixadm ~]$ ls /dev | grep cua
cuaU0.0
cuaU0.0 .init
cuaU0.0.lock
cuaU0.1
cuaU0.1.init
cuaU0.1.lock
cuaU0.2
cuaU0.2.init
cuaU0.2.lock

UPD (спс. Fantom & Евгений):
Чтобы небыло проблем с доступом к устройству модема, добавим юзера asterisk в группу dialer

pw groupmod dialer -m asterisk

Для отправки команд модему можно использовать утилиту cu:

 cu -l /dev/cuaU0.0 -s 9600

Для выхода из терминала можно нажать тильда-точка-enter «~.».

Модем разлочен, голосовые функции активированы, определяется в системе, настроим его в качестве GSM-шлюза:
Качаем исходники, собираем модуль chan_datacard (у меня собрался только с Asterisk 1.6, при установленном 1.4 сборка вываливается с ошибкой)

cd ~
svn co http://www.makhutov.org/svn/chan_datacard/trunk/ chan_datacard
cd ~/chan_datacard/
./configure
make install
cp ~/chan_datacard/etc/datacard.conf /usr/local/etc/asterisk

Вот мой datacard.conf:

[general]
interval=15 

[datacard0]
context=datacard-incoming           ; Контекст для входящих с GSM шлюза, понадобится в extensions.ael
audio=/dev/cuaU0.1                  ; Порт для голосовых функций
data=/dev/cuaU0.2                   ; Порт для AT команд
group=1                             ; Группа звонка
rxgain=2                            ; Увеличение входящей громкости
txgain=2                            ; Увеличение исходящей громкости
autodeletesms=yes                   ; Автоматически удалять входящие смс
resetdatacard=yes                   ; Сбросить модем во время инициализации
u2diag=0                            ; Установить U2DIAG параметр (0 = отключить все кроме функций модема)
usecallingpres=yes                  ; Предоставлять информацию о CallerID или нет
callingpres=allowed_passed_screen   ; Установить СallerID presentation
disablesms=yes                      ; chan_datacard в данный момент глючит с приемом SMS. Когда SMS приходит во время звонка
                                    ; модем может упасть. Включите эту опцию чтобы отключить прием SMS.
                                    ; Default = no

Перегружаем Asterisk из консоли (подключение к консоли: asterisk -r) командой core restart now
Смотрим, загрузился ли модуль (module show like chan_datacard):

nixadm*CLI> module show like chan_datacard
Module                         Description                              Use Count
chan_datacard.so               Datacard Channel Driver                  0
1 modules loaded
nixadm*CLI>

Проверяем подключился ли модем (datacard show devices):

nixadm*CLI> datacard show devices
ID           Group State      RSSI Mode Submode Provider Name  Model      Firmware          IMEI             IMSI             Number
datacard0    1     Free       12   5    4       MTS RUS        E1550      11.608.12.00.143  352************  250************  Unknown

Теперь можно вписать наш GSM шлюз в диалплан. Вот так примерно это делается в extensions.ael (это пример, рабочая версия позже):

// Исходящие через GSM-GW
context mobile-numbers {
        _89XXXXXXXXX => Dial(Datacard/datacard0/${EXTEN},,T);
};

// Входящие с GSM-GW
context datacard-incoming {
    s =>.
    {
           Dial(SIP/10&SIP/11&SIP/12,60,t); // При звонке на модем, звонят телефоны с внутренними
                                                        // номерами 10,11,12, тайм аут 60, t - указывает что вызов может быть переведен
    };
}

Настраиваем GSM-Шлюз Linksys SPA3102
О настройке Linksys SPA3102 отдельная заметка, читать тут.

Настраиваем фаервол:
SIP:
Так как SIP является RTP протоколом, то кроме порта сигнализации (по умолчанию 5060 TCP или UDP), нужно также открыть порты для медиа-трафика (UDP). Их диапазон настраивается в файле конфигурации Asterisk rtp.conf, вот часть конфига, которая отвечает за это:

rtpstart=10000 ; Начальный порт
rtpend=20000  ; Конечный порт

IAX2:
Тут проще: сигнализация и данные идут в одном потоке — нужно открыть один UDP порт (по умолчанию 4569).

  1. Alexx
    9th Ноябрь 2011 в 14:53

    Да, очень познавательная статья!Спасибо все по ней и настроил!
    ТОлько вот не могу понять на счет перевода звонка, у меня номер 6011 и 6017, я с 6017 звоню на 6011 и набираю во время разговора *; Attended transfer! и вот что выводит консоль:
    == Using SIP RTP CoS mark 5
    — Called 6011
    — SIP/6011-00000014 is ringing
    — SIP/6011-00000014 answered SIP/6017-00000013
    — Started music on hold, class ‘default’, on SIP/6017-00000013
    — Playing ‘pbx-transfer.gsm’ (language ‘ru’)
    [2011-11-09 14:41:38] WARNING[4304]: features.c:1615 builtin_atxfer: Extension ‘6’ does not exist in context »
    — Playing ‘pbx-invalid.alaw’ (language ‘ru’)
    — Stopped music on hold on SIP/6017-00000013
    == Spawn extension (macro-stdexten-followme, s, 3) exited non-zero on ‘SIP/6017-00000013’ in macro ‘stdexten-followme’
    == Spawn extension (macro-stdexten, s, 6) exited non-zero on ‘SIP/6017-00000013’ in macro ‘stdexten’
    == Spawn extension (default, 6011, 1) exited non-zero on ‘SIP/6017-00000013’
    и звонок возращается обратно 6011
    Dial поставил Tt, и все равно не работает…
    НО еще проблема в том, что у меня только не переводят звонки эти номера, остальные работаю нормально…Помогите пожалуйста, всю голову уже сломал никак не выходит(((

  2. dimia
    8th Декабрь 2011 в 14:27

    Спасибо за автору за статью! и одна маленькая просьба — выложить рабочие конфиги куда-нибудь в виде архива. здесь в тексте они неправильно отображаются … заранее спасибо!

  3. Сергей Занковецкий
    6th Январь 2012 в 12:32

    Тоже присоединяюсь к предыдущему посту о не читаемости конфигов, из-за наличия хтмл тегов в статье. Прошу помочь с данной проблемой. Спасибо

  4. 6th Январь 2012 в 20:22

    @Сергей Занковецкий
    @dimia
    Была проблемка с плагином, вроде сейчас все верно. У меня сейчас не осталось уже Asterisk, поэтому конфиги выложить не могу.

  5. Сергей Занковецкий
    7th Январь 2012 в 14:49

    Да, теперь — порядок, спасибо.

  6. 17th Март 2012 в 16:43

    Здравствуйте.
    Уже выражал автору огромную благодарность за статью.
    Теперь хотел бы задать вопрос.
    Решаема ли проблема с непрохождением dtmf при звонке на gsm-шлюз?
    Поставил у себя Huawei E1550 от мегафона. Модем разлочен, голос активирован. Все работает (хотя качество голоса можно было бы и получше) кроме dtmf.
    Как побороть? Или я один такой?
    Помогите пожалуйста. DTMF очень нужен.

Страницы комментариев
Вы должны авторизоваться для отправки комментария.