Elastix и chan_datacard
Когда стоит задача поднять asterisk и мы знаем о том что будут использоваться USB-3G-модемы для голоса, это не проблема, достаточно поставить его на дистрибютиве с новым ядром и потом можно прикрутить панель типа FreePBX или asterisk-gui. Но что делать когда уже стоит готовая сборка Elastix, с кучей настроек и в продакшене, а прикрутить chan_datacard просто необходимо. В этой статье я опишу всю процедуру от начала и до конца для Elastix 2.3.
Первая проблема это старое ядро 2.6.18 в Elastix, а chan_datacard обещает работать нормально с ядром не меньше 2.6.33. Можно конечно взять исходники необходимого ядра и скомпилировать его, но учитывая то, что Elastix был установлен на дохленький Atom и предвидя 2-3 часа компиляции, я отказался от этого варианта (мы ведь помним, АТС в продакшене, и простой должен быть минимальным) и пошел другим путем, установив новое ядро из добавленного репозитария.
Добавляем репозитарий ELRepo:
# rpm --import http://elrepo.org/RPM-GPG-KEY-elrepo.org
# rpm -Uvh http://elrepo.org/elrepo-release-5-4.el5.elrepo.noarch.rpm
проверяем в /etc/yum.repos.d/elrepo.repo в разделе [elrepo-kernel] — enabled=1
Актуальное ядро в этом репозитории на момен написания этой статьи 3.0.88, нам вполне подходит.
Устанавливаем необходимые пакеты и новое ядро:
# yum install gcc glibc-devel glibc-headers subversion
# yum install --skip-broken kernel-lt kernel-lt-devel kernel-lt-headers
Я добавил —skip-broken потому что ругалось на kernel-lt-headers.
меняем в /boot/grub/menu.lst:
default=1
на
default=0
видим что у нас появилось там новое ядро:
title CentOS (3.0.88-1.el5.elrepo)
root (hd0,0)
kernel /vmlinuz-3.0.88-1.el5.elrepo ro root=LABEL=/
initrd /initrd-3.0.88-1.el5.elrepo.img
обновляем grub — подставляем свой диск вместо /dev/sda:
# grub-install /dev/sda
Ядро установили, но перезагружаться рано, надо скачать все необходимое и подготовить, что бы после перезагрузки сразу начать компиляцию необходимых модулей, так как после загрузки в новое ядро не будет работать dahdi (если Вы не используете этот модуль можно смело перезагружаться и продолжать).
необходимо скачать dahdi отсюда — на момент написания актуальная версия 2.7:
# cd /usr/src
# wget http://downloads.asterisk.org/pub/telephony/dahdi-linux-complete/dahdi-linux-complete-current.tar.gz
# tar -xzvf dahdi-linux-complete-current.tar.gz
# ln -s dahdi-linux-complete-2.7.0+2.7.0 dahdi
так же нам понадобиться поддержка oslec для DAHDI(модуль эхо-подавления), скачиваем:
# svn co http://svn.astfin.org/software/oslec/trunk/ oslec
далее переходим к подготовке DAHDI:
# mkdir -p /usr/src/dahdi/linux/drivers/staging/echo
# cp -fR /usr/src/oslec/kernel-test/* /usr/src/dahdi/linux/drivers/staging/echo/
# cp -fR /usr/src/kernels/3.0.88-1.el5.elrepo-x86_64/drivers/staging/echo/* /usr/src/dahdi/linux/drivers/staging/echo/
Скачиваем исходники chan_datacard
# svn co http://www.makhutov.org/svn/chan_datacard/trunk/ /usr/src/chan_datacard
Собственно, мы все подготовили, можем перезагружаться в новое ядро.
сразу после перезагрузки надо скомпилировать DAHDI:
# cd /usr/src/dahdi
# make
# make install
# make config# /etc/init.d/asterisk stop
# /etc/init.d/dahdi restart
# /etc/init.d/asterisk start
все должно пройти без проблем, смотрим:
# asterisk -vvvvr
CLI> dahdi show channels
получаем примерно такое:
На данный момент мы поставили новое ядро и драйвера DAHDI, то есть вернули систему в рабочее состояние но с новым ядром. Теперь необходимо установить chan_datacard:
# cd /usr/src/chan_datacard
# ./configure
# make
# make install
# cp etc/datacard.conf /etc/asterisk/
на 64-битной системе установка не произошла, потому как была попытка произвести установку в /usr/lib/asterisk/modules , а такого пути нет, копируем модуль ручками:
# cp chan_datacard.so /usr/lib64/asterisk/modules/
Я использовал модем huawei E1750 не привязанный к оператору, девственный как слеза 🙂 Стоит упомянуть, что перед использование модем необходимо переключить в режим «только модем», отправив ему команду AT^U2DIAG=0, через minicom например.
Переходим к настройке chan_datacard:
в файле /etc/asterisk/datacard.conf комментируем или удаляем все после строки:
;-----------------------------------------------------------------------------------
и вставляем:
[datacard1]
context=from-gsm ; context для входящих звонков
audio=/dev/ttyUSB1 ; tty порт для аудио подключения
data=/dev/ttyUSB2 ; tty порт для управляющих AT комманд модема
group=4 ; Группа вызова
rxgain=6 ; Изменение громкости динамика
txgain=6 ; Изменение громкости микрофона
resetdatacard=yes ; Перезагрузка модема при перезапуске модуля
autodeletesms=yes ; Удаление смс с симкарты при перезапуске
usecallingpres=yes ; use the caller ID presentation or not
callingpres=allowed_passed_screen ; set caller ID presentation
сохраняем и закрываем файл.
переходим к редактированию файла /etc/asterisk/extensions_custom.conf , добавляем в конец файла:
[from-gsm]
exten => s,1,goto(from-trunk,${IMEI},1)
exten => sms,1,Verbose(Incoming SMS from ${CALLERID(num)} ${SMS})
exten => sms,n,System(echo '${STRFTIME(${EPOCH},,%Y-%m-%d %H:%M:%S)} -%- ${DATACARD} -%- ${CALLERID(num)} -%- ${SMS} -%-' >> /var/log/asterisk/sms.txt)
exten => sms,n,Hangup()
exten => ussd,1,Verbose(Incoming USSD: ${USSD})
exten => ussd,n,System(echo '${STRFTIME(${EPOCH},,%Y-%m-%d %H:%M:%S)} -%- ${DATACARD} -%- USSD -%- ${USSD} -%-' >> /var/log/asterisk/ussd.txt)
exten => ussd,n,Hangup()
Этот context будет принимать звонки и подставлять IMEI модема вместо DID, так что мы запросто сможем создавать правила входящих звонков прямо в панели управления Elastix. Так же будут приниматься смс и ussd и складываться в файлы /var/log/asterisk/sms.txt и /var/log/asterisk/ussd.txt соответственно. Позже мы будем обрабатывать данные файлы и посылать письма с пришедшими sms и ussd.
Осталось перезапустить asterisk:
# /etc/init.d/asterisk restart
заходим в консоль asterisk-а и проверяем:
# asterisk -vvvr
CLI> datacard show device datacard1
получим примерно следующее, не торопитесь, карта должна успеть инициализироваться 1-2 минуты:
Осталось прикрутить исходящие звонки, для этого заходим в панель Elastix-а , добавляем Custom Trunk:
И добавляем в Outbound Routes новое правило для звонков:
Теперь при выходе через 8, мы будем звонить через наш 3G-модем.
для настройки входящих звонков, делаем правило, используя в качестве DID IMEI-номер модема, и принимаем звонки по своему желанию, на номер, группу или очередь.
Следующим этапом будет настройка отправки пришедших смс и ussd на почту. Здесь я воспользовался скриптом отсюда, чуть изменив.
Создаем файл /var/www/html/sms.php следующего содержания — не забудьте изменить адрес отправки:
<?php
// путь до файла с смс
$file_name = '/var/log/asterisk/sms.txt';
// если размер файла больше 0 переходим к его обрботке
if (filesize($file_name)>0) {
// открываем файл для чтения и записи
$fobj = fopen($file_name,"r+");
$text = fread($fobj, filesize($file_name));
// в файл смс записываются в следуюшем формате
// дата и время -%- datacard -%- номер отправителя -%- текст смс
// формат и разделитель можно сменить в конфиг файле
// преобразовываем содержимое в массив используя -%- как разделитель
$text1 = explode("-%-", $text);
// делим массив а части по 4 элемента
$array = array_chunk($text1, 4);
// рисуем таблицу в которой будут выводится наши смс
$mes1 = "<table border='1' bordercolor='#000000' cellspacing='0' cellpadding='2'>";
$mes2 = "<tr align='center' valign='middle
'><td width='160'>Дата и время сообщения</td><td width='140'>Номер отправителя</td><td width='230'>Сообщение</td></tr>";
for ($i = 0; $i <= count($array)-2; $i++)
{
$sms = $array[$i];
$sms1[] = "<tr valign='middle
'><td align='center'>".$sms[0]."</td><td align='center'>".$sms[2]."</td><td align='left'>".$sms[3]."</td></tr>";
}
for ($mes3 = '', $j = 0; $j <= count($sms1); $j++)
{
$mes3 = $mes3.$sms1[$j];
}
$mes4 = "</table>";
// Теперь перейдем к отправке
// отправлять будем используя встроенную функцию mail()
// получаем текущую дату
$date = date("d.m.Y");
// получаем текущее время
$time = date("H:i:s");
// адрес куда будем отправлять письмо
$to = "your@email.com";
// тема письма
$subject = "New SMS Message(s) - ".$date." - ".$time;
// текст письма
$message = $mes1.$mes2.$mes3.$mes4;
// дополнительные заголовки письма
// кодировка письма
$headers = "Content-type: text/html; charset=utf-8 \r\n";
// отправитель письма
$headers .= "From: SMS Gate <sms@your-domain.org>\r\n";
// отправляем письмо, если отправка прошла успешно
// выводим сообщение иочищаем файл с смс
if (mail($to, $subject, $message, $headers)) {
echo "Soobshenie ojidaet otpravki";
ftruncate ($fobj, 0);
} else {
// если ошибка выводим это сообщение
echo "Oshibka pri otpravke";
}
// закрываем файл
fclose($file_name);
}
// если файл пустой выводим это сообщение
else { echo "Sms soobshenii net..."; }
?>
ставим задание в cron, каждые 5 минут делать проверку:
*/5 * * * * /usr/bin/wget --no-check-certificate -O /dev/null -q https://192.168.10.190/sms.php
Посылаем sms на вставленный в модем номер и ждем пока нам придем сообщение на почту. Всё должно пройти успешно.
Аналогичный файл создаем для ussd, меняя тему письма, например. Теперь Вы можете полноценно использовать свой номер в модеме, звонить и принимать звонки, принимать смс. Отправлять смс можно командой в панели Elastix-а или в asterisk-cli, либо написать свой фротн-энд для этого, но текст должен содержать только латиницу. Входящие же sms могут быть и на кириллице.
Отправить sms или ussd можно следующим образом, из asterisk-cli или панели Elastix-а (PBX->Tools->Asterisk-Cli)
datacard ussd datacard1 *100#
datacard sms datacard1 +79261001001 Test hello
После пары дней тестов, я заметил, что один раз модем завис, посему наваял простенький скрипт для проверки регистрации модема и сброса его в случае её отсутствия, создаем файл /root/bin/test-restart-dc со следующим содержимым:
#!/bin/bash
dclogfile=/root/bin/dc.log
dcdate=`/bin/date "+%Y-%m-%d %H:%M:%S"`
dcstatus=`/usr/sbin/asterisk -rx "datacard show device datacard1"|grep "GSM Registration Status"|grep "Registered"`
RES1=$?
if [ $RES1 -ne 0 ];then
/usr/sbin/asterisk -rx "datacard reset datacard1"
echo "Reset "$dcdate >>$dclogfile
echo $dcdate" -%- datacard1 -%- RESET -%- Datacard Resetted -%-" >> /var/log/asterisk/ussd.txt
exit 1
else
echo "OK "$dcdate >>$dclogfile
exit 0
fi
выставляем ему права на выполнение chmod a+x /root/bin/test-restart-dc ,и ставим в крон, проверку каждый час:
3 * * * */root/bin/test-restart-dc
В случае, если скрип отработает reset карты, Вам придет письмо на почту, для этого просто добавляется строка в файл /var/log/asterisk/ussd.txt , который мы обрабатываем выше. Так же результаты проверки пишутся в лог — /root/bin/dc.log , с указанием даты и времени.
Вот и всё!
Удачи в настройках.
© shadow_alone
i translated ur topic to english and applied it on my server , it works like a charm 🙂
thank you from egypt:)
U R welcome!
hi andery ,it causes kernel panic when detecting digium cards !!
did you test it using sangoma or digium cards in your server ??
Добрый день. Нуждаюсь в помоще по привязке двух USB-3G-модемов к Elastix 2.4, Сообщите пожалуйста сколько будет стоить Ваша работа. Заранее благодарен. Андрей. 95123 @ tut.by
Подскажите, в чем ошибка?
[root@localhost chan_datacard]# svn co http://www.makhutov.org/svn/chan_datacard/trunk/ /usr/src/chan_datacard
Checked out revision 192.
[root@localhost chan_datacard]# ./configure
configure complete, now type ‘make’
[root@localhost chan_datacard]# make
gcc -Wall -Wextra -fPIC -DAST_MODULE=»chan_datacard» -D_THREAD_SAFE -I. -I/usr/include -O2 -DICONV_CONST=»» -D__DEBUG__ -D__APP__ -c chan_datacard.c
In file included from chan_datacard.c:69:
chan_datacard.h:257: предупреждение: initialization from incompatible pointer type
In file included from chan_datacard.c:78:
__app.c: In function ‘app_status_exec’:
__app.c:17: предупреждение: пропущен инициализатор
__app.c:17: предупреждение: (где-то рядом с инициализацией для ‘args.device’)
__app.c: In function ‘app_send_sms_exec’:
__app.c:71: предупреждение: пропущен инициализатор
__app.c:71: предупреждение: (где-то рядом с инициализацией для ‘args.device’)
In file included from chan_datacard.c:85:
__channel.c: In function ‘channel_new’:
__channel.c:18: предупреждение: implicit declaration of function ‘ast_channel_linkedid’
__channel.c:18: предупреждение: несоответствие типов указатель/целое в условном выражении
__channel.c:22: предупреждение: implicit declaration of function ‘ast_channel_tech_pvt_set’
__channel.c:23: предупреждение: implicit declaration of function ‘ast_channel_tech_set’
__channel.c:24: предупреждение: implicit declaration of function ‘ast_format_set’
__channel.c:24: предупреждение: implicit declaration of function ‘ast_channel_readformat’
__channel.c:25: предупреждение: implicit declaration of function ‘ast_channel_writeformat’
__channel.c:26: предупреждение: implicit declaration of function ‘ast_format_cap_add’
__channel.c:26: предупреждение: implicit declaration of function ‘ast_channel_nativeformats’
__channel.c:30: предупреждение: implicit declaration of function ‘ast_channel_rings_set’
__channel.c:50: предупреждение: implicit declaration of function ‘ast_channel_language_set’
__channel.c: In function ‘channel_request’:
__channel.c:95: предупреждение: implicit declaration of function ‘ast_format_cap_iscompatible’
__channel.c:98: предупреждение: passing argument 3 of ‘ast_getformatname_multiple’ makes integer from pointer without a cast
__channel.c: In function ‘channel_call’:
__channel.c:360: предупреждение: implicit declaration of function ‘ast_channel_tech_pvt’
__channel.c:360: предупреждение: initialization makes pointer from integer without a cast
__channel.c:381: предупреждение: implicit declaration of function ‘ast_channel_state’
__channel.c:383: предупреждение: implicit declaration of function ‘ast_channel_name’
__channel.c:383: предупреждение: format ‘%s’ expects type ‘char *’, but argument 6 has type ‘int’
__channel.c:396: предупреждение: format ‘%s’ expects type ‘char *’, but argument 8 has type ‘int’
__channel.c:402: предупреждение: implicit declaration of function ‘ast_channel_connected’
__channel.c:402: ошибка: неверный аргумент для ‘->’
__channel.c: In function ‘channel_hangup’:
__channel.c:438: предупреждение: initialization makes pointer from integer without a cast
__channel.c: In function ‘channel_answer’:
__channel.c:482: предупреждение: initialization makes pointer from integer without a cast
__channel.c: In function ‘channel_digit_begin’:
__channel.c:504: предупреждение: initialization makes pointer from integer without a cast
__channel.c: In function ‘channel_read’:
__channel.c:530: предупреждение: initialization makes pointer from integer without a cast
__channel.c:544: предупреждение: implicit declaration of function ‘ast_channel_fdno’
__channel.c:554: предупреждение: implicit declaration of function ‘ast_format_copy’
__channel.c:554: ошибка: ‘union ast_frame_subclass’ has no member named ‘format’
__channel.c: In function ‘channel_write’:
__channel.c:654: предупреждение: initialization makes pointer from integer without a cast
__channel.c:658: ошибка: ‘union ast_frame_subclass’ has no member named ‘format’
__channel.c:660: предупреждение: format ‘%s’ expects type ‘char *’, but argument 7 has type ‘int’
__channel.c: In function ‘channel_fixup’:
__channel.c:741: предупреждение: initialization makes pointer from integer without a cast
__channel.c: In function ‘channel_indicate’:
__channel.c:792: предупреждение: initialization makes pointer from integer without a cast
__channel.c: In function ‘channel_queue_hangup’:
__channel.c:879: предупреждение: implicit declaration of function ‘ast_channel_hangupcause_set’
chan_datacard.c: In function ‘unload_module’:
chan_datacard.c:621: предупреждение: implicit declaration of function ‘ast_format_cap_destroy’
chan_datacard.c: In function ‘load_module’:
chan_datacard.c:685: предупреждение: implicit declaration of function ‘ast_format_cap_alloc’
chan_datacard.c:690: предупреждение: assignment makes pointer from integer without a cast
make: *** [chan_datacard.o] Ошибка 1
Возможно ядро 3.2, поэтому дает такую ошибку.
Добрый день!
Получилось у Вас собрать таки модуль chan_datacard? Столкнулся с такой же проблемой что и ваша…