Home » Technology » Telephony (Page 84)

Category Archives: Telephony

The Most Versatile VoIP Provider: FREE PORTING

Newbie’s Guide to Asterisk@Home 2.4: Unabridged Soup-to-Nuts Installation Guide

Want a rock-solid PBX at a rock-bottom price: free! Well, it's almost Ground Hog's Day so here we go again! Asterisk@Home 2.4 has hit the street because of a serious bug-fix release of Asterisk®. Now you get version 1.2.3 of Asterisk, and you also get the latest and greatest version of Linux, CentOS 4.2; the latest Festival Speech Engine (1.96); the latest version of the Asterisk Management Portal (1.10.010); the Flash Operator Panel (version 0.24); Open A2Billing; Digium card auto-configuration; NVfaxdetect support; loads of AGI scripts including weather forecasts and wakeup calls; xPL support; the SugarCRM Contact Management System with the Cisco XML Services interface and Click-to-Dial support; plus lots more. And, yes, it still fits on a single CD! Note: A new version of Asterisk (1.2.4) was released yesterday to fix a serious memory leak so you can expect yet another version of Asterisk@Home and yet another soup-to-nuts tutorial shortly. Simply stated, don't put this version into production without at least implementing the Bleeding Edge 1.2.4 upgrade at the end of this article ... but we're making progress.

Editor's Note: This version of Asterisk@Home has been superceded. For the latest tutorial on or after March 13, click here.

The installation process is pretty straightforward. You download the 2.4 ISO image from here, burn a CD (click here if you need a refresher course), use an old clunker PC or a $200 WalMart special (see inset), insert the CD you made, plug your machine into the Internet and turn it on. Then watch while Asterisk@Home loads CentOS/4.2 and all the Asterisk and Linux goodies imaginable: Apache, SendMail, Comedian Mail, SugarCRM, MySQL, PHP, phpMyAdmin, SSH, Bluetooth, the Asterisk Management Portal, the Flash Operator Panel, Call Detail Reporting, and on and on. We've covered how to use most of the Linux products in our Mac HOW-TO's (see sidebar), and they work exactly the same way with Asterisk@Home so keep reading. And, yes, this install will reformat (aka ERASE) your hard disk before it begins, but it now warns you first.


Loading CentOS/4 and Asterisk 1.2.3. Here's how the 2.4 install went for us, and we'll walk you through getting everything set up so that it can be used as a production server. Once the install begins, you can expect to eat up about 25 minutes with the CentOS 4.2 install. The install CD then will eject itself, reboot the system, and begin the Asterisk compile and installation. That takes about 25 more minutes to complete.

Securing Your Passwords. When it's finished and reboots, log in as root with password as your password. Type help-aah for a listing of the five passwords that need to be changed. Change them all NOW!

passwd
passwd admin
passwd-maint
passwd-amp
passwd-meetme

Getting the Latest CentOS Updates. Once your system is secure, load all of the application updates for CentOS 4.2. There now are over 50 updates and installs so be patient. The update command to issue is yum -y update.

Activating Bluetooth Support. Once the updates are completed, activate Bluetooth support if you plan to use it with our Follow-Me Phoning proximity detection application. Run setup, down arrow to System Services, press ENTER, down arrow to bluetooth and press the space bar, tab to OK, press ENTER, tab twice to Quit and press ENTER.

Rebuilding Zaptel. First, reboot your system: shutdown -r now. Because a new version of the kernel is installed as part of the yum update, you'll need to rebuild support for ZAP devices. Log in as root and type the following command: rebuild_zaptel. Then reboot your system: shutdown -r now. Now log in as root again and type genzaptelconf. Reboot once more and you're all set to go: shutdown -r now. You only need to rebuild Zaptel when there is a kernel update as there was with this yum update.

Simplifying SSH. If you're going to be connecting to other servers from your new Asterisk@Home 2.4 system using SSH or SCP, then build your new RSA key pair now. This lets you use SSH and SCP (secure copy) without having to enter a password each time. You can also automate backups and proximity detection scripts as we've explained previously here. Log in to your new Asterisk@Home 2.4 server as root. From the command prompt, issue the following command: ssh-keygen -t rsa. Press the enter key three times. You should see something similar to the following. The file name and location in bold below is the information we need:

Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /root/.ssh/id_rsa.
Your public key has been saved in /root/.ssh/id_rsa.pub.
The key fingerprint is:
1d:3c:14:23:d8:7b:57:d2:cd:18:70:80:0f:9b:b5:92 root@asterisk1.local

Now copy the file in bold above to your other Asterisk servers, Linux machines, and Macs. There's probably a way on PCs as well, but I've given up on that platform particularly after Sony's latest security stunt so you're on your own there. From your Asterisk@Home 2.4 server using SCP, the command should look like the following (except use the private IP address of each of your other Asterisk or Linux servers instead of 192.168.0.104). Provide the root password to your other servers (one at a time) when prompted to do so.

scp /root/.ssh/id_rsa.pub root@192.168.0.104:/root/.ssh/authorized_keys

On a Mac running Mac OS X, the command would look like this (using your username and your Mac's IP address, of course):

For user access only: scp /root/.ssh/id_rsa.pub wardmundy@192.168.0.104:/Users/wardmundy/.ssh/authorized_keys
For full root access: scp /root/.ssh/id_rsa.pub root@192.168.0.104:/var/root/.ssh/authorized_keys

Once the file has been copied to each server, try to log in to your other server from your Asterisk@Home 2.4 server with the following command using the correct destination IP address, of course:

ssh root@192.168.0.104

You should be admitted without entering a password. If not, repeat the drill or read the complete article and find where you made a mistake. Now log out of the other server by typing exit.


Installing WebMin. We don't build Linux systems without installing WebMin, the Swiss Army knife of the Linux World. You can use it to start and stop services, check logs, adjust startup scripts, manage cron jobs, babysit your SendMail server, and many, many other tasks that are downright painful without it. If you ever need help from others, WebMin is a great tool for letting others help you.

There are lots of ways to install WebMin. We prefer the easy way which is to issue the following commands at a Linux prompt after logging in as root. Note: WebMin updates come out all the time. If you want to be sure you start with the latest and greatest version, go to their web site first and write down the number of the current version. Then substitute it below when issuing these commands. Note that there is a new version of WebMin since our previous article on Asterisk@Home 2.2 was published.

cd /root
mkdir webmin
cd webmin
wget http://internap.dl.sourceforge.net/sourceforge/webadmin/webmin-1.260-1.noarch.rpm
rpm -Uvh webmin*


WebMin runs its own web server on port 10000. To start WebMin, issue this command: /etc/webmin/start. You access it with a web browser pointed to the IP address of your Asterisk box at that port address, e.g. http://192.168.0.108:10000. The login name is root. Then type in your root password and press enter. The main WebMin screen will display. We really don't want the WebMin server starting up each time the OS reboots so do the following. Once you're logged in to WebMin, choose System->Bootup and Shutdown and then click on webmin. Click the No button beside Start at boot time, and then click the Save button. Before we forget, we need to also make one change to the new Asterisk@Home configuration to avoid problems down the road. The default RTP listening ports for Asterisk@Home are set to 10000 to 20000 so there's a conflict on port 10000 with WebMin. Log in as root and, using an editor, call up the rtp.conf file: nano /etc/asterisk/rtp.conf. Now change the rtpstart port from 10000 to 10001 and save the change: Ctrl-W, Y, and press Enter. Then restart Asterisk: amportal restart. Finally, to stop WebMin when you're finished using it, issue this command: /etc/webmin/stop. You can start it any time you need it, and then use a web browser to access it. But there's no need to consume processing resources running a second web server when you're not using it.

IP Configuration for Asterisk. We need a consistent IP address or domain name both on your internal network and externally if you expect to receive incoming calls reliably. There are three pieces to the IP configuration: (1) setting the internal IP address of your Asterisk server, (2) configuring an external qualified domain name which will always point to your router/firewall, and (3) configuring your router to transfer incoming Asterisk packets to your Asterisk server. Log into your server as root using your new password. Now type ifconfig eth0 (that's "e-t-h-zero") then enter, and write down both your inet addr and your HWaddr on the Ethernet 0 interface, eth0. Inet addr is the internal IP address of your Asterisk box assigned by your DHCP server (i.e. your router/firewall). HWAddr is the MAC address of your Asterisk server's eth0 network card. To assure a consistent internal IP address, you can either configure your router/DHCP server to make certain that it always hands out this same address to your Asterisk machine, or you can manually configure an IP address for this machine which is not in the range of addresses used by your DHCP server. Almost all routers now make it easy to preassign DHCP addresses so we prefer option 1. It's generally under the tab for LAN IP Setup and is generally called something like Reserved IP table. Just add an entry and call it Asterisk PBX and specify the IP address and MAC address that you wrote down above. Now each time you reboot your Asterisk server, your router will assign it this same IP addreess. To assure a consistent external address is a little trickier. Unless you have a static (fixed) IP address, you'll want to use a Dynamic DNS service such as dyndns.org and configure your router to always advertise its external IP address to dyndns.org. DynDNS.org will take care of revising the IP address associated with your domain name when your ISP changes your dynamic IP address. Then you can configure your VoIP provider account using your fully-qualified dyndns.org domain name, e.g. windswept.dyndns.org provides access to our beach house network even though Time Warner cable hands out dynamic IP addresses which change from time to time. Finally, you'll need to log into your router and redirect certain incoming packets to the internal IP address of your Asterisk machine. If you want external access to the Apache web server on your Asterisk machine, then map TCP port 80 to the internal IP address of your Asterisk system. For WebMin external access, map TCP port 10000 to your Asterisk system. If you want remote access to your Asterisk system via SSH, then map TCP port 22 to the internal IP address of your Asterisk system. If you want external IP phones or other Asterisk servers to be able to communicate with your Asterisk system, then map the following UDP port ranges to the internal IP address of your Asterisk system:

SIP 5004-5082
RTP 10001-20000
IAX 4569

For more details, read our article on the subject.

Basic System Configuration. To get a basic Asterisk system up and running, you only need to do a few things. First, you need an Outbound Trunk to actually deliver your outbound calls to Plain Old Telephones (POTS). Second, you need to configure an Outbound Route to tell Asterisk which trunk to use to deliver your outbound calls to the intended recipients. Third, you need to configure at least one extension so that you can plug in some sort of telephone instrument to place and receive calls using your new Asterisk server. The phone can be a hardware device such as an IP telephone or a POTS phone, or it can be a software device such as a free IP softphone. The advantage of IP telephones and softphones is that they require no additional hardware besides your Asterisk server. A POTS phone or our favorite, a 5.8GHz wireless phone system with up to 10 extensions, both require an additional piece of hardware although some of the newer IP wireless phones give you the best of all worlds (see inset). To use a POTS phone, the hardware required is either a circuit board with an FXS port or an external black box (ATA device) such as a Sipura SPA-1001. If you also want to connect your Ma Bell phone line to your Asterisk server, then you need a circuit board with an FXO port or an external black box (ATA device) such as a Sipura SPA-3000. Our favorite is the SPA-3000 because it has both FXO and FXS ports in a box the size of a pack of cigarettes for under $100.


Setting Up An Outbound Trunk. You configure an outbound trunk using your web browser and the Asterisk Management Portal (AMP). But first, you have to have an account with a service provider. This is the company that carries your calls from your Asterisk server to plain old phones in your neighbor's house or Aunt Betty's in California. With VoIP, it's a good idea to have two providers, but today let's start with one. We'll save you some time and lots of money. Unless you make substantial international calls regularly, use TelaSIP/VoipExpress. If you want to know why, read the full article here. Or just try a free call for yourself using our server. Basically, $5.95 a month gets you a local number in your choice of area code with free incoming calls, and 2¢ per minute for outbound calls to anywhere in the U.S. $9.95 a month buys you all of that plus free outbound calls in the area code of the phone number you select. $14.95 a month gets you a number in the area code of your choice with unlimited incoming calls and unlimited outbound calls to anywhere in the U.S. There are no sneaky add-on fees and no obnoxious terms of service. Just be sure to tell them to configure your account for use with Asterisk. They also have very reasonable business plans. If, on the other hand, you'd prefer to try another provider, take a look at our easy setup guides for most of the major VoIP providers here.

Once you have your account name and password, point your web browser to the IP address of your new Asterisk@Home 2.4 server and log in as maint with the password you selected. Then choose AMP->Setup->Trunks->Add SIP Trunk assuming you're using TelaSIP. NOTE to existing users: if you already have an Asterisk server using your TelaSIP account, don't use the same account at the same time on your new Asterisk@Home 2.4 server! Plug in the CallerID number you were assigned for your account. Set Maximum Channels to 2. For the Dial Rules, use the following (substituting your local area code for 404 below):

1|NXXNXXXXXX
NXXNXXXXXX
404+NXXXXXX

In the Outgoing Settings section, name your trunk telasip-gw. Then enter the following for the Peer Details using your own account name for username and fromuser and using your own assigned password for secret:

context=telasip-in
dtmfmode=rfc2833
fromuser=youraccountname
host=gw3.telasip.com
insecure=very
secret=yourpassword
type=peer
username=youraccountname

Leave the Incoming Settings section blank, and in the Registration String, enter the following using your account name and password:

youraccountname:yourpassword@gw3.telasip.com

Click the Submit Changes button, and then click the red bar to reload Asterisk. Now we need to add the context which will actually process the incoming calls from TelaSIP. Choose AMP->Maintenance->Config Edit->extensions_custom.conf and add the following code at the bottom of the file substituting your new phone number for 4041234567. Save the file and reload Asterisk to finish the setup. See the Comments to this post for a more versatile approach which will let you use your TelaSIP line for Ring Groups.

[telasip-in]
exten => 4041234567,1,NoOp(Incoming call on TelaSIP #4041234567)
exten => 4041234567,2,Dial(local/200@from-internal,20,m)
exten => 4041234567,3,VoiceMail(200@default)
exten => 4041234567,4,Hangup

Configuring an Outbound Route. Now we need to tell Asterisk where to send our outbound calls when we dial them. To get started, we'll just send everything to the TelaSIP trunk we just configured. Choose AMP->Setup->Outbound Routing->Add Route. For Route Name, use Outside. Leave the password blank. For Dial Patterns, enter the following:

NXXXXXX
NXXNXXXXXX
1NXXNXXXXXX

For the Trunk Sequence, choose SIP->telasip-gw from the drop-down list. Then click Submit Changes. Be sure you also delete the sample outbound route that came with the install, or your outbound calls may go nowhere. Finally, click the red bar to save your new Outbound Routing setup.


Configuring an Extension. You have to have an extension to make and receive calls with Asterisk@Home so let's build one. Choose AMP->Setup->Extensions->SIP to begin. For the Extension Number, let's use 200 to keep things simple. For the Display Name, make up something that tells where this phone will be located, e.g. Kitchen. For the Outbound CID, use 200. For secret, make up a password for this extension. For Voicemail and Directory, choose Enabled. Plug in your password again. Type in your email address, and, if you want to also be paged when you get a new voicemail, type in a pager email address. Click the Yes button beside Email Attachment, and leave the other settings alone. Now click the Submit button and then click the red bar to save your changes and reload Asterisk.


Downloading a Free Softphone to Test Asterisk. Unless you already have an IP phone, the easiest way to get started and make sure everything is working is to install an IP softphone. You can download a softphone for Windows, Mac, or Linux from CounterPath. Or download the pulver.Communicator. Both are free! Just install and then configure with the IP address of your Asterisk@Home 2.4 server. For username and password, use your extension number and password from above. Once you make a few test calls, don't waste any more time. Buy a decent SIP telephone. We think the best value in the marketplace with excellent build quality and feature set is the under $100 GrandStream GXP-2000. It has support for four lines, speaks CallerID numbers, has a lighted display, and can be configured for autoanswer with a great speakerphone. Short of paying three times as much, that's as good as desktop phones get. If you want to use Asterisk throughout your home, buy a good 5.8GHz wireless phone system with plenty of extensions (our two favorites are shown in the insets below) and then purchase an SPA-3000 to connect up both your home phone line and all your cordless phones. Our tutorial will show you how. The final option is to use a wireless IP phone which is the best of both worlds, a cordless phone that talks IP telephony without an ATA blackbox such as the Uniden UIP1868 (see also insets above).


Activating Email Delivery of VoiceMail Messages. When you're out and someone leaves you a voicemail message, Asterisk@Home will let you forward that voicemail message to your email address as a .wav file which can be played within most email client software. Or you can have Asterisk@Home send an instant message to your cell phone or pager telling you who called, what their phone number was, and how long a voicemail message the person left for you. Or you can do both. In addition, you can tell Asterisk@Home whether to delete the voicemail from your Asterisk server after sending it to your email account. In short, you now can manage all of your incoming email and voicemail from a single place, your email client. In order to send out emails from your Asterisk@Home server, you'll need to make a few changes. First, make this adjustment to the /etc/hosts file on the server. Since anonymous emails are blocked by most ISPs, you'll need a fully-qualified domain name for your server. If you don't have your own domain, the easiest alternative is to use the fully-qualified domain name that your ISP assigns to the IP address for your broadband connection. Don't forget to update it when your ISP changes your IP address! To find out what your fully-qualified domain name is, go to a command prompt on your Asterisk server and type: nslookup 123.456.789.001 substituting your public IP address for the preceding numbers. Then write down the name entry without the trailing period. Now edit the hosts file: nano /etc/hosts. Move the cursor to the second line which reads 127.0.0.1 asterisk1.local , and then move the cursor over the first letter of the first domain name shown, usually asterisk1.local. Now type in the fully-qualified domain name you previously wrote down and add a space after your entry. Don't erase the existing entry! Save your settings: Ctrl-X, y, enter. Now restart network services on your Asterisk machine: service network restart. Next, you need to modify the email message which delivers your voicemails so that it includes your fully-qualified domain name. Don't do this in AMP, or you'll mess up the formatting of the email message. You can download a fresh copy here if you need it. Instead, use nano: nano -w /etc/asterisk/vm_email.inc. Press Ctrl-W, type /cgi, and press the enter key. You're now positioned where you need to type either the fully-qualified domain name for your Asterisk server or the private IP address if you only want to read your emails from behind your firewall. When you start typing, the text display is going to jump all over the place because of word wrap. Don't freak out. You haven't messed anything up. Once you complete your entry, don't erase or change anything else. Save the file: Ctrl-X,Y, then enter. Now go into AMP->Maintenance->Config Edit->vm_general.inc with a web browser. Change the serveremail entry to an email name at the fully qualified domain you used in your /etc/hosts file above. Then save your configuration and restart Asterisk. If you continue with this setup and still don't receive emails, here's another configuration change that is sometimes necessary. On the Asterisk terminal, log in as root. Switch to the directory where the SendMail configuration file is stored: cd /etc/mail. Make a backup of the config file: cp sendmail.cf sendmail.cf.bak. Then issue the following command: echo CGasterisk.dyndns.org >> sendmail.cf. Substitute the actual domain name of your Asterisk server for asterisk.dyndns.org, but be sure it's preceded by CG with no intervening spaces.Then reboot your server and try again: shutdown -r now. Finally, if your ISP doesn't permit downstream mail servers (that's you), then take a look at this link which will show you how to designate your ISP as your SMTP smart host using SendMail.


To configure the voice mail forwarding options, go into the Setup tab of the Asterisk Management Portal using a web browser. Click on Extensions and then click on an extension you already have configured. In the Voicemail and Directory section of the form, enter either (or both) your email address and your pager or cellphone's text messaging address. To email the voicemails as attachments, just click Yes beside Email Attachment. To delete the voicemail message from your voicemail inbox after sending it to your email address (not recommended until you first get it working correctly), click Yes beside Delete Vmail. For those using a dynamic IP address with phones at remote locations connecting to your Asterisk server, we'll show you how to automate the process of changing your Asterisk server's IP address in a future column.

Call Recording Fixed. This update fixes inbound and outbound call recording which now works reliably. You can set your preferences for call recording when you set up each extension. The recordings are stored in /var/spool/asterisk/monitor unless you set other preferences in agents.conf.

Paging Fixed. If you want to use paging with your Asterisk system, the sound card problems in Asterisk@Home 2.1 and Asterisk 1.2 have been resolved. Review this posting on SourceForge for additional details. It now works with full and half-duplex sound cards. Thanks, Tracy!

Wakeup Calls Good News & Bad News. The good news is that Asterisk@Home 2.4 includes a new version of the Wakeup Call program, and the default setup works. The bad news is that the "annoy" option which is supposed to force the user to key in a number to prove they're awake still doesn't work. And, the wakeup call application now uses a different method of scheduling wakeup calls which broke our TeleYapper and Telephone Reminder applications. Mental note for would-be developers, don't rely on someone else's code to keep your code working reliably. This is especially true in the open source marketplace. Everyone is full of new ideas, and they don't necessarily stop to think of the impact the improvements will have on other folks' applications. We'll have new versions of our applications ... soon. Bear with us while we hold off for Asterisk@Home 2.5 just so we don't have to repeat the drill again.

Adjusting Call Parking Extensions. Traditionally, pressing the pound key (#) and dialing extension 700 parked a call on Asterisk@Home systems and notified you of the parked extension. The call then could be picked up by dialing the parked extension in the range of 701 to 799 from any extension on your system. The new setup is to press # and dial 70 to park a call and 71-79 to pick it up. If you prefer the old setup, you'll need adjust the settings in features.conf. Go to AMP->Maintenance->Config Edit->features.conf and make it look like this:

[general]
parkext => 700 ; What ext. to dial to park
parkpos => 701-799 ; What extensions to park calls on
context => parkedcalls ; Which context parked calls are in

Directory Lookup Fixed. Pressing the pound key (#) from any phone connected to your Asterisk server now calls up a directory lookup function using the Asterisk Management Portal (AMP).

Max Channels Bug Remains. A bug has been reported because of a deprecated command that makes Asterisk@Home's calculation of maximum channels invalid. To fix it, goto AMP->Maintenance->Config Edit->extensions.conf->macro-dialout-trunk and comment out line s,7 so that it looks like this:

;exten => s,7,CheckGroup(${OUTMAXCHANS_${ARG1}})

Then insert a new line s,7 just below it which looks like this:

exten => s,7,GotoIf($[ ${GROUP_COUNT()} > ${OUTMAXCHANS_${ARG1}} ]?108)

Then click the Update button and reload Asterisk to activate the change.

Syntax Error in extensions.conf Causes Failed Incoming Calls. One of our readers has discovered a nasty little bug in extensions.conf that will cause some incoming calls to fail. Edit extensions.conf->macro-dial and change line s,22 as follows. Then reload Asterisk.
Before:

exten => s,22,GotoIf($[$[${HuntMembers} >= 1]?30 )

After:

exten => s,22,GotoIf($[$[${HuntMembers}] >= 1]?30 )

Tweaking SIP.conf. There are a few changes we recommend you make in the [general] context of the sip.conf file. Using the Asterisk Management Portal, go to AMP->Maintenance->Config Edit->sip.conf. It's a good idea to include your actual CallerID number of your default outbound trunk here instead of Default. We also recommend that you add allow=gsm just below the existing allow=alaw line in the file. You may also want to include progressinband=yes which assures that callers will hear ring tones when placing calls even if your provider doesn't provide them. This is a fairly common complaint with BroadVoice in particular. Don't forget to reload Asterisk once you make these changes: AMP->Setup->Incoming Calls->Submit Changes and then click the Red bar.

Connecting Remote Extensions or a Remote Asterisk Server. If you plan to connect remote extensions to your Asterisk server, then add the following entries to sip.conf above using your own fully-qualified domain name and the first three octets of the private IP address of your Asterisk server:

externip = myasteriskbox.dyndns.org
localnet=192.168.0.0/255.255.255.0
nat=yes

You'll also either need to define your Asterisk server as a DMZ device in your firewall setup, or you can open the following UDP ports and map all of them to the private IP address of your Asterisk box: 4569, 5004-5082, and 10000-20000. If you only hear half of a conversation with a remote extension, it's usually a NAT problem meaning you probably forgot to do the port mapping drill. We plan to cover remote extensions and interconnecting Asterisk servers in more detail in a future column so stay tuned. In the meantime, if you have a dynamic DNS connection that changes your IP address regularly and you don't want to wrestle with manually updating your Asterisk server each time there's a change, just download chandave's sip reload script and copy it to your /etc/asterisk directory. Then execute the following commands while logged in as root:

chown asterisk:asterisk /etc/asterisk/sip_reload.sh
export EDITOR=nano
crontab -e
00,05,10,15,20,25,30,35,40,45,50,55 * * * * root /bin/sh /etc/asterisk/sip_reload.sh >/dev/null 2>&1

Save your crontab addition in the usual nano way, and you're all set: Ctrl-X, y, then enter. This clever little script will make sure your Asterisk server always knows its own IP address regardless of how often your ISP changes it. And you'll never lose inbound connectivity from remote extensions or servers for over 5 minutes. If you'd like to read the full discussion, visit this link on the Voxilla forum.

Managing Incoming Calls. For long time readers of this column, you already know that our recommended strategy for handling incoming calls is to set up a simple Stealth AutoAttendant. Basically, this is a welcome message that greets your callers and then transfers them to an extension or ring group of your choice. The advantage of this approach is that it also lets callers like you press buttons to navigate through various options on your Asterisk system without advertising them to the public at large. If you're just getting started with Asterisk, you can read all about setting up a Stealth AutoAttendant here. If you'd prefer to manage your incoming calls with AMP, you'll still need to fix the [from-sip-external] context in the extensions.conf file, or all your incoming SIP and IAX calls will ring busy. To fix it, choose AMP->Maintenance->Config Edit->extensions.conf->from-sip-external. Comment out all the lines in the existing file by adding a semicolon at the beginning of each line. Then add the following line, save your changes, and reload Asterisk.

exten => _.,1,Goto(from-pstn-timecheck,s,1)


New Custom Speed Dialing. Asterisk@Home 2.4 has a built-in speed dialing utility. The reserved speed dial numbers are 300 to 399. Adding a number to your speed dial list is easy. Just pick up an extension and dial 300-3xx-6781234567 where 3xx is the speed dial code you want to create and 6781234567 is the phone number you want dialed when you enter the speed dial code. Just make sure you enter the number to be called in a format that is supported by your Asterisk dialplan, i.e. if outside calls need to be preceded by a 1 or a 9, then the number should be entered in a matching format. You can look up speed dial numbers by dialing an asterisk followed by the 3-digit speed dial code, e.g. *301 would tell you the number stored in speed dial 301. If you need additional flexibility with both web browser and phone access as well as 1 to 5-digit speed dial codes, download our free AsteriDex robodialer.

Fixed A2Billing: Asterisk Calling Card Platform. This web-based application allows you to generate and issue calling cards to individuals so that they can place calls remotely through your Asterisk server. If you've always wanted to be just like AT&T, here's your Big Chance! There's very little that you can do with an AT&T calling card that can't be done as well or better by you using A2Billing. And, it won't take an M.B.A. to undercut AT&T's calling card rates and still make buckets of money. All you need now are a few customers. Heck, I'll sign up with you. I sign up for everything. But first, a word of caution. Assuming your Asterisk server has web exposure on the Internet, you need to secure the admin and root passwords in this application whether you use it or not. To access the application, go to http://192.168.0.104/a2billing/ using the actual internal IP address of your Asterisk server. Log in as root with a password of myroot. Click on the ADMINISTRATOR tab in the left column and then click Show Administrator. Now click on the Edit button beside each of the two administrator accounts and change the passwords to something secure. If you really would like to learn more about it, documentation for the application is available here. And, if you decide to use the application, you'll need to uncomment the 6 actual dialplan lines in extensions_custom.conf and reload Asterisk:

;[custom-callingcard]
;exten => s,1,Answer
;exten => s,2,Wait,2
;exten => s,3,DeadAGI,a2billing.php
;exten => s,4,Wait,2
;exten => s,5,Hangup

Footnote: The missing A2Billing code from Asterisk@Home 2.1 has been added. You can read all about the problem here. There's also a pretty good step-by-step setup guide for Asterisk@Home here.

SugarCRM Contact Management. Asterisk@Home includes the latest and greatest version of the best open source contact management application on the planet, SugarCRM. You access the application with a web browser: http://192.168.0.104/crm/ substituting the private IP address of your Asterisk box, of course. Specify admin for your username and password for your password. Whether you use the application or not, change the admin password. It's easy. Just click the Administrator link under Welcome admin. Then click the Change Password button. Complete documentation for the application is available here. If contact management is your thing, knock yourself out, and we'll talk to you next spring when you finish getting everything set up to run your business. It's a great product, but be prepared to invest lots of time in the project if you expect to use it productively.

Incoming Fax Support. The NVfaxdetect software that we showcased back in December, 2005, now is included in Asterisk@Home 2.4. The major advantage of NVfaxdetect is that it works with SIP and IAX trunks as well as ZAP lines. Unfortunately, you'll still need to manually configure the Asterisk Management Portal to use it. Thanks to Thunderbird in Australia, it's pretty easy to make the necessary changes. First, enable faxing support. Go to AMP->Setup->General Settings and set Extension of Fax Machine to system. Then fill in your email address. Click the Submit Changes button to save your changes. This sets the default route for incoming faxes. If you want to specify different fax destinations for different DID trunks, then set up Inbound Routing using AMP and define the fax extension as system (instead of default) with an email address for delivery of the fax for each particular DID. You'll also need to make sure you have outbound email functioning on your Asterisk server (see above), or none of the rest of this matters. NVfaxdetect converts your incoming faxes to PDF documents and then emails them to you. So the next step is to get that conversion functionality working. Log in to your Asterisk server as root and type the following command: install-pdf. Now make a backup copy of your extensions.conf file: cp /etc/asterisk/extensions.conf /etc/asterisk/extensions.conf.bak. We're going to do some heavy editing of the extensions.conf file so be careful with your cutting-and-pasting: nano -w /etc/extensions.conf. When you're finished, save your changes and restart Asterisk: amportal restart.

Find and delete the entire [from-pstn-reghours] context. Replace it with the following:

[from-pstn-reghours]
exten => s,1,GotoIf($[${FAX_RX} = disabled]?from-pstn-reghours-nofax,s,1:2) ; if fax detection is disabled, then jump to from-pstn-nofax - else continue
exten => s,2,Answer
exten => s,3,Playtones(ring) ; play fake ring so caller doesn't wonder what's going on
exten => s,4,NVFaxDetect(4) ; detect faxes while playing ring sound - goes to "fax" extension if detected
exten => s,5,SetVar(intype=${INCOMING})
exten => s,6,Cut(intype=intype,-,1)
exten => s,7,GotoIf($[${intype} = EXT]?8:9) ; If INCOMING starts with EXT, then assume its an extension
exten => s,8,Goto(ext-local,${INCOMING:4},1)
exten => s,9,GotoIf($[${intype} = GRP]?10:11) ; If INCOMING starts with GRP, then assume its a ring group
exten => s,10,Goto(ext-group,${INCOMING:4},1)
exten => s,11,GotoIf($[${intype} = QUE]?12:13)
exten => s,12,Goto(ext-queues,${INCOMING:4},1)
exten => s,13,Goto(${INCOMING},s,1) ; not EXT or GR1 - it's an auto attendant
exten => fax,1,Goto(ext-fax,in_fax,1)
exten => h,1,Hangup

Find and delete the entire [from-pstn-afthours] context. Replace it with the following:

[from-pstn-afthours]
exten => s,1,GotoIf($[${FAX_RX} = disabled]?from-pstn-afthours-nofax,s,1:2) ; if fax detection is disabled, then jump to from-pstn-nofax - else continue
exten => s,2,Answer
exten => s,3,Playtones(ring) ; play fake ring so caller doesn't wonder what's going on
exten => s,4,NVFaxDetect(4) ; detect faxes while playing ring sound - goes to "fax" extension if detected
exten => s,5,SetVar(intype=${AFTER_INCOMING})
exten => s,6,Cut(intype=intype,-,1)
exten => s,7,GotoIf($[${intype} = EXT]?8:9) ; If INCOMING starts with EXT, then assume its an extension
exten => s,8,Goto(ext-local,${AFTER_INCOMING:4},1)
exten => s,9,GotoIf($[${intype} = GRP]?10:11) ; If INCOMING starts with GRP, then assume its a ring group
exten => s,10,Goto(ext-group,${AFTER_INCOMING:4},1)
exten => s,11,GotoIf($[${intype} = QUE]?12:13)
exten => s,12,Goto(ext-queues,${AFTER_INCOMING:4},1)
exten => s,13,Goto(${AFTER_INCOMING},s,1) ; not EXT or GR1 - it's an auto attendant
exten => fax,1,Goto(ext-fax,in_fax,1)
exten => h,1,Hangup

Find and delete the entire [ext-fax] context. Replace it with the following:

[ext-fax]
exten => s,1,Answer
exten => s,2,Goto(in_fax,1)
exten => in_fax,1,StopPlaytones ; you must do this or it will play ring sounds over your fax
exten => in_fax,2,GotoIf($[${FAX_RX} = system]?3:analog_fax,1)
exten => in_fax,3,Macro(faxreceive)
exten => in_fax,4,Hangup
exten => analog_fax,1,GotoIf($[${FAX_RX} = disabled]?3:2) ;if fax is disabled, just hang up
exten => analog_fax,2,DBGet(DIAL=DEVICE/${FAX_RX}/dial);
exten => analog_fax,3,Dial(${DIAL},20,d)
exten => analog_fax,4,Hangup
exten => out_fax,1,txfax(${TXFAX_NAME}|caller)
exten => out_fax,2,Hangup
exten => h,1,system(tiff2ps -2eaz ${FAXFILE} | ps2pdf - ${FAXFILE}.pdf)
exten => h,2,system(mime-construct --to ${EMAILADDR} --subject "Fax from ${CALLERIDNUM} ${CALLERIDNAME}" --attachment ${CALLERIDNUM}.pdf --type application/pdf --file ${FAXFILE}.pdf)
exten => h,3,system(rm ${FAXFILE} ${FAXFILE}.pdf)
exten => h,4,Hangup()

Be aware that NVfaxdetect can be a bit quirky. You may need to fine tune the timing mechanism in the actual NVfaxdetect(4) lines above. If 4 is too long or too short a delay to detect a fax call, try 10 or nothing at all. 10 works for us. 4 works in Australia apparently. If you need more details, read our original article on this topic. It also explains how to get all of this working with the Nerd Vittles Stealth AutoAttendant if you're using it.

Asterisk Recording Interface. A new web-based utility for managing your voicemail now is included in AMP and can be accessed by clicking Voicemail & Recordings on the initial AMP screen at the IP address of your Asterisk system. It also can be accessed at http://Asterisk-IP-address/recordings/. To log in to the Asterisk Recording Interface (ARI), just enter an extension number on your Asterisk system and the password for that extension. From the web interface, you can manage your voicemail messages including playing them back, you can review the call log to this extension, and you can set your voicemail password as well as your desired setup for recording calls by setting the call monitor defaults for this extension. Very cool!

Other Out-of-the-Box Utilities. Asterisk@Home 2.4 comes bundled with a number of additional utilities. Here are some of them. You can retrieve the current time by dialing *60. If the time is wrong, you can reset your default time zone by logging into your server as root and typing config. A current weather report for New York is available by dialing *61. You can change the city by following our previous tutorial which is available here. Something has come unglued in the festival script, however, because there is a noticable 10-second delay between each line of text that is read now. To set up a wakeup call from any extension, dial *62. To determine the phone number of any extension, just dial *65. You can use the default MeetMe conferencing system from any or all of your extensions by dialing 8 plus the number of an existing extension. Additional conference rooms can be added by editing meetme_additional.conf. Finally, you can record customized voice prompts for your system by dialing 5678 from any extension. Before this will work, edit the extensions_custom.conf file (AMP->Maintenance->Config Edit->extensions_custom.conf) and uncomment the seven lines shown below which are located at the bottom of the file. Just remove the leading semicolons. You'll also need to uncomment the following line near the top of file at the beginning of the [from-internal-custom] context: ;include => custom-recordme.

;[custom-recordme]
;exten => 5678,1,Wait(2)
;exten => 5678,2,Record(/tmp/asterisk-recording:gsm)
;exten => 5678,3,Wait(2)
;exten => 5678,4,Playback(/tmp/asterisk-recording)
;exten => 5678,5,Wait(2)
;exten => 5678,6,Hangup

Once you make a recording, it needs to be moved to /var/lib/asterisk/sounds/custom with a new filename.gsm, e.g. mv /tmp/asterisk-recording.gsm /var/lib/asterisk/sounds/custom/hihoney.gsm. Then change the ownership of the file: chown asterisk:asterisk /var/lib/asterisk/sounds/custom/hihoney.gsm. You then can play the recording with a line like this in your dialplan: exten=>s,1,Playback(custom/hihoney) where hihoney is the name you assigned to the recording without its .gsm extension.

Where To Go From Here. After you get a functioning Asterisk system, you're ready to move on to some really cool things that make Asterisk a one-of-a-kind PBX. There are customized weather reports, web and phone-based dialers from a MySQL address book, incoming fax to PDF conversion with email delivery, blacklisting of telemarketers, bluetooth proximity detection so that your home or office calls automatically transfer to your cellphone when you depart with your bluetooth device, and on and on. You'll also want to take a more in-depth look at many of the topics we've covered above. For a complete catalog of all of our Asterisk projects and everything else we've written about Asterisk@Home, go here. Then take a look at a terrific writeup from the other side of the globe: Asterisk@Home for Dumb-Me. Finally, there's an Asterisk@Home Handbook Wiki project under development that's worth a careful look.

The Bleeding Edge. For the pioneers that just can't wait for Asterisk@Home 2.5, here's a quick and dirty way to load the new, new Asterisk 1.2.4 today. Log in to your new Asterisk@Home 2.4 server as root. Then issue the following commands in order. Be aware that this probably will break a few things including inbound fax support. But you can follow our original fax tutorial to get that working again. Everything else seems to work just fine at least in our Zap-free test machine. Enjoy!

amportal stop

cd /usr/src
wget http://ftp.digium.com/pub/zaptel/zaptel-1.2.3.tar.gz
wget http://ftp.digium.com/pub/libpri/libpri-1.2.2.tar.gz
wget http://ftp.digium.com/pub/asterisk/asterisk-1.2.4.tar.gz
wget http://ftp.digium.com/pub/asterisk/asterisk-addons-1.2.1.tar.gz
wget http://ftp.digium.com/pub/asterisk/asterisk-sounds-1.2.1.tar.gz

tar -zxvf zaptel-1.2.3.tar.gz
tar -zxvf libpri-1.2.2.tar.gz
tar -zxvf asterisk-1.2.4.tar.gz
tar -zxvf asterisk-addons-1.2.1.tar.gz
tar -zxvf asterisk-sounds-1.2.1.tar.gz

cd zaptel-1.2.3
make clean
make install
cd ..

cd libpri-1.2.2
make clean
make install
cd ..

cd asterisk-1.2.4
make clean
make install
cd ..

cd asterisk-addons-1.2.1
make clean
make install
cd ..

cd asterisk-sounds-1.2.1
make clean
make install
cd /root

amportal start

As Easy As 1-2-3: A Telephone Reminder System for Asterisk

It's free software day again at Nerd Vittles, and today we're building a telephone reminder system which lets you schedule reminders for future events. And when the appointed date and time arrives, Asterisk® will swing into action and place a call to the number you designate to deliver your customized reminder message. Asterisk@Home is bundled with a wakeup call system which lets you schedule one wakeup call per extension for any time during the next 24 hours. It's a great product. However, after using a terrific, web-based email reminder system in the Linux and Mac worlds, we wanted something a bit more flexible and robust for Asterisk. For those using Asterisk@Home 2.5 or later, click here for the updated version.

Some of the smarts for this system already have been incorporated into our TeleYapper Voice Messaging System. But that was a real-time system meaning it began calling immediately after you chose a group of people to call. This system is different in that you can schedule the calls for the near or distant future, you can specify different numbers for the calls, and you can customize the recorded message for each call. In short, it's perfect for appointment reminders, birthday reminders, anniversary reminders, and anything else you want to remember. You can even schedule a call to your cellphone to remind you to pick up little Suzie at school on Friday after Band Camp. And all it takes is a phone call to set up each reminder. There's no web page to fill in and no database required to manage the reminders. You can schedule as many reminders per phone number as your little fingers care to dial! Did we mention? It's also FREE! And to all you freeloaders, yes, some of your fellow readers chipped in to pay for the Allison IVR prompts so those are included at no cost as well.

Prerequisites. As with all of our newer projects, we've tested this with ISO-installed versions of Asterisk@Home 1.5 and Asterisk@Home 2.2. If you wish to use it with some other version of Asterisk@Home or with a "pure Asterisk" system, then you're on your own. We're also assuming you have a working wakeup call system on your Asterisk box. Dial *62 from any extension on your system, and enter a wakeup call for a few minutes in the future, and be sure you get a return call on schedule. If not, get that installed and working first. Here's a link to our Asterisk@Home 2.2 installation tutorial if you need assistance. Once all the prerequisites are satisfied, this project ought to take you about 30 minutes ... if you're a fast reader.


How It Works. The reminder system is actually quite simple to use. You dial extension 1-2-3 on your Asterisk system, enter your password, and then you'll be prompted to record a reminder message. Next you enter the phone number, date, and time for delivery of the reminder message. When the appointed date and time arrives, Asterisk will place the call to the number you specified using your default dialing rules and will play the customized reminder when the call is answered. If the call is not answered, the call will be repeated n number of times with a delay between calls of x minutes before giving up on the call. You'll get an email with the call reminder setup if desired. You also get to configure the number of retries and the delay between calls.

Finally, a word about failed calls. Reminders are important to most folks, or you wouldn't be scheduling them. So failed calls are problematic. On the one hand, you don't want to overburden your phone system placing thousands of reminder calls just because the calls continue to fail. You may have entered an incorrect phone number, for example. So our middle-of-the-road solution to failed calls is this. You can tell the system how many times to repeat the call and how much time to eat up between attempts. If the call still fails, the reminder will be deleted from the system. But the reminder message is preserved. If you look in /var/lib/asterisk/sounds/custom on your system, you will see some custom sound files (such as the reminder prompts which all begin with reminder). In addition, you will see the actual reminder messages for each of your reminders. The naming convention is HourMinute.Date.PhoneNumber.gsm. If you see entries in this directory with dates before today's date, those are failed call reminders. You can play the sound files on most computers by simply double-clicking on the files. You can delete old reminders while logged in as root on your Asterisk system with a command like this:

rm -f /var/lib/asterisk/sounds/custom/*.20060123.*.gsm

Just be sure you don't delete today's reminder messages or messages with a future date, or your entire reminder system will be up in smoke!


Here are the components that make up the complete system:

  • AutoAttendant Contexts to Create Reminder
  • Code Snippet to Answer 1-2-3 Calls
  • Allison Voice Prompts for the Reminder IVR Interface
  • checkdate.php AGI script to Check for Dates in the Past
  • checktime.php AGI script to Check for Times in the Past
  • reminder.php AGI script to Schedule Calls
  • Reminder Call Processing Contexts
  • run_reminders script to Check for Reminders
  • Limitations. There are a few limitations you need to be aware of. First, you can't schedule delivery of a reminder within the first 5 minutes after midnight each night. That's when the reminder system does its housekeeping. Second, you must schedule reminders at least 5 minutes after you place your call to set up the reminder. This gives the system ample time to generate the configuration files it needs and to put them in the right places. Third, the current reminder system does not fully support simultaneous scheduling of multiple reminders. The current system uses unique names to identify sound files generated by multiple simultaneous callers. However, it still is theoretically possible for two different callers to schedule two reminders for the same phone number and the same reminder time and to do so at the exact same time. This would cause one of the reminders to be discarded. We just wanted to alert you to this remote possibility. But we hasten to add that the chance of this happening is pretty small even in a very large Asterisk system with hundreds of users. There's also no present ability to link to a database for scheduling reminders. Nor can you schedule recurring reminders for events such as birthdays and anniversaries. But we'll get to some of these features one day soon if there's sufficient interest in the product.

    License. This software is licensed for your use under a Creative Commons Attribution-ShareAlike 2.5 license. Before using this software, please read the terms of the license. A Plain English version of the license is available here. You may not charge a fee for something we are giving you for free. Finally, we ask that you preserve our copyright notice in any copies of the software you make. The same applies to derivative works. If you do not accept the terms of the license, do not use the software. Even if you accept the terms of the license, keep in mind that BY USING THIS SOFTWARE, YOU ASSUME ALL RISKS OF USE AND NO WARRANTIES EXPRESS OR IMPLIED ARE PROVIDED WITH THIS FREE SOFTWARE INCLUDING FITNESS FOR A PARTICULAR USE AND MERCHANTABILITY. In short, it's up to you to determine, at your risk, whether this software meets your needs. Don't assume that it will, and don't assume that the code is error-free. It's probably not.


    The Game Plan. To get the Reminder System set up, we're first going to move all of the code into the proper places. This includes the modifications to the dialplan contexts, installation of the new Allison voice prompts, and installation of the PHP/AGI scripts. Then we'll walk you through configuring the system. And finally we'll schedule a reminder to make sure everything went according to plan.

    Modifying Your Dialplan. Step #1 is to cut-and-paste some code into your dialplan. For those using Asterisk@Home or the Asterisk Management Portal (AMP), this code should go at the bottom of the extensions_custom.conf file in the /etc/asterisk directory on your system. If you're using Asterisk@Home 1.5, use this code. If you're using Asterisk@Home 2.x, use this code. For Step #2, you'll need to cut-and-paste the following code snippet into the top section of extensions_custom.conf in the [from-internal-custom] context:

    exten => 123,1,Answer
    exten => 123,2,Wait(1)
    exten => 123,3,Authenticate(12345678)
    exten => 123,4,Goto(reminder,s,1)

    If, for some reason, you already are using extension 1-2-3 on your Asterisk system for some other purpose, then simply adjust the 123 extension in the four lines above to another number that works on your system. This is the number you will dial to schedule reminders. Line 3 is important as well. This is where you set a password for scheduling reminders on your system. Anyone that knows the password can schedule reminders. Simply replace 12345678 with a password that's secure enough for you to sleep well.

    Finally, a head's up. When you do the cut-and-paste, double-check the long lines of code such as h,1 in [reminder9] and be sure all of the text ends up on a single line. Otherwise, things won't work correctly. Once you've added the two sections of code, save extensions_custom.conf and reload Asterisk.

    Installing Reminder Voice Prompts. As some of you know, we mounted a campaign to raise a little dough to pay for Allison, the Voice of Asterisk, to record custom voice prompts for the new reminder system. To all that contributed, thank you! And, we have some money remaining to cover some things we'll be telling you about in the coming weeks. Incidentally, it's not too late to donate $5 if you are so inclined. All of the donations will be used to defray future Asterisk development project costs. It also guarantees you free voice prompts in future projects. At least for this project, the voice prompts are free for the taking subject to the terms of the license agreement whether you contributed or not. Just log into your Asterisk server as root and enter the following commands:

    cd /var/lib/asterisk/sounds/custom
    wget http://nerdvittles.com/reminder_voice.zip
    unzip reminder_voice.zip
    rm reminder_voice.zip
    chmod 664 reminder*.gsm
    chown asterisk:asterisk reminder*.gsm


    Installing the Reminder PHP/AGI Scripts. While you're still logged in as root, let's install the final pieces of code that you'll need to get things working. Just execute the commands below which match the version of Asterisk@Home you're running.

    For Asterisk@Home 1.x users:

    cd /var/lib/asterisk/agi-bin
    wget http://nerdvittles.com/aah15/reminder.zip
    unzip reminder.zip
    rm reminder.zip
    chmod 775 reminder.php
    chown asterisk:asterisk reminder.php
    chmod 775 check*.php
    chown asterisk:asterisk check*.php
    chmod 777 run_reminders
    chown asterisk:asterisk run_reminders

    For Asterisk@Home 2.x users:

    cd /var/lib/asterisk/agi-bin
    wget http://nerdvittles.com/aah2/reminder.zip
    unzip reminder.zip
    rm reminder.zip
    chmod 775 reminder.php
    chown asterisk:asterisk reminder.php
    chmod 775 check*.php
    chown asterisk:asterisk check*.php
    chmod 777 run_reminders
    chown asterisk:asterisk run_reminders

    Creating Reminders Directory. While you're still logged in as root, create the directory to store your reminders until the day arrives to execute them. Just issue the following commands:

    su asterisk
    cd /var/spool/asterisk
    mkdir reminders
    exit

    Setting Up the Reminder Crontab Entry. Now we need to set up the cron job to actually move reminders into the wakeup calls directory on the day they are scheduled to run. While logged in as root, edit the crontab file: nano -w /etc/crontab. Be sure you typed the exit command in the last step, or you'll be logged in as asterisk instead of root. And you won't be able to edit the file! Now cut-and-paste the following command to the bottom of the crontab file. It should go on its own line immediately below the run_wakeups line which already is in the file and is used to schedule your wakeup calls.

    0 0 * * * root /var/lib/asterisk/agi-bin/run_reminders >/dev/null 2>&1

    Once you've added the line, save your changes: Ctrl-X, Y, then press Enter. Whew! That's it for the Reminder code. Now let's configure the system, and you'll be all set.


    Configuring the Reminder System. To configure the reminder system, you'll need to edit the reminder.php script while logged in as root: nano -w /var/lib/asterisk/agi-bin/reminder.php. You'll notice a section of variables at the top of the file that looks like this:

    $extensionmaxdigits=4 ;
    $debug = 1;
    $newlogeachdebug = 1;
    $emaildebuglog = 0;
    $email = "yourname@yourdomain" ;
    $trunk = "local" ;
    $callerid = "6781234567" ;
    $numcallattempts=6 ;
    $calldelaybetweenruns=300 ;
    $timetoring=40 ;
    $acctcode= "Reminder" ;

    This is the only section of code you ought to mess with. When we update the code in coming months to add features, we'll assume everything else has been left intact. Be very careful when editing this file. Don't remove any semicolons or quotation marks, or nothing will work! Here's a quick run-down on what each of the above variables does:

  • $extensionmaxdigits=4 ... Sets the maximum number of digits for treating outbound calls as calls to local extensions.
  • $debug = 1 ... If set to 1, then a debug log is created in /var/log/asterisk/reminder.txt. Instructions for deleting reminders are in the log.
  • $newlogeachdebug = 1 ... If set to 1, then a new debug log is created each time a reminder is scheduled. Otherwise, file grows and grows.
  • $emaildebuglog = 0 ... If set to 1, the debug log is emailed to the email address set below when each reminder is scheduled.
  • $email = "yourname@yourdomain" ... Enter your actual email address between the quotation marks. Only works if line above is set to 1.
  • $trunk = "local" ... If set to "local", calls are routed using your default dialplan rules. Otherwise, specify a trunk to use, e.g. "sip/telasip-gw".
  • $callerid = "6781234567" ... Specify your caller ID number. Only used if $trunk is not set to "local" above.
  • $numcallattempts=6 ... If there is no answer on the Reminder call, how many times should Asterisk attempt to deliver the reminder?
  • $calldelaybetweenruns=300 ... How many seconds delay should there be between failed call attempts to deliver a reminder?
  • $timetoring=40 ... How many seconds should the call ring when attempting to deliver a reminder?
  • $acctcode= "Reminder" ... What accouting code should be used for reminder calls?
  • Once you've configured the Reminder System to meet your needs, save your changes: Ctrl-X, Y, then press Enter. HINT: You may want to start with the defaults which will work well for most folks.


    Scheduling A Reminder. We're ready to take the Reminder System for a trial run at this juncture. Make sure you've reloaded your Asterisk system, and then dial 123 from an extension on your system. Enter the password you set up for your system and then press the pound key.

    Entering a Reminder Message. You'll first be prompted to record a reminder message. This is the message that will be played when someone answers the reminder call. If you're not scheduling this reminder for yourself, then the message ought to explain who's calling and what the purpose of the call is. Once you've recorded your message, press the pound key to end the recording. You can replay or rerecord the reminder if desired while you're in this step of the reminder creation process.

    Entering the Callback Number. When prompted for the reminder callback number, there are a couple of things to keep in mind. First, if you've specified "local" as the trunk to use for reminders in the reminder.php script, then the phone numbers can be entered in any format supported by your dialplan. Press the pound key after entering the appropriate number. The calls will be placed using the trunks specified in your dialplan rules. The one exception is extensions on your local Asterisk system since these can't be routed by Asterisk@Home using your outbound calls dialplan rules. The way we handle extensions is by examining the length of the phone number. At the top of reminder.php, you can specify the maximum number of digits for local extensions by setting $extensionmaxdigits. So long as the callback number is less than or equal to this number of digits, the system has the smarts to correctly route the call to a local extension.

    If you have designated a particular trunk for placement of reminder calls, then you'll need to make certain that the format of the phone numbers entered for reminders on your system matches a dial string supported for this outbound trunk in your dialplan. For example, if this trunk requires that calls be entered with a 1 and then an area code and 7-digit number, then that is the only format that should be used for entering callback numbers in your reminder system. Again, the one exception is calls to local extensions. So long as the number of a local extension is entered with less than or equal the number of digits set for the $extensionmaxdigits variable in reminder.php, the call will be routed properly to the local extension regardless of the trunk setting.

    Finally, here's a shortcut that can be used if the phone you're using to schedule the reminder is the same one on which you want to get the reminder callback. In this case, just press the pound key when prompted for the number to which to deliver the reminder message. This will set the callback number as the caller ID of the phone you used to schedule the call. If it's a local extension, then the caller ID will be set to the local extension number of the phone from which you placed the reminder scheduling call. Just be sure your $extensionmaxdigits is set correctly or calls to local extensions will fail.

    Entering the Date of the Reminder. Once you accept the reminder message, you'll be prompted to enter the date on which this reminder will be delivered. Dates are entered using a four-digit year, then a two-digit month, and then a two-digit day. There is some error correction but not much. You obviously can't schedule reminders in the past! And you don't need to press the pound key after entering the eight digits. Once you've entered a date, the system will tell you what date you entered including the day of the week. If the entry is correct, just press 1 to move on.

    Entering the Time of the Reminder. Now you'll be prompted to enter the delivery time for your reminder. Times are entered as a two-digit hour and two-digit minute. For times less than 1200, you will be prompted whether you meant AM or PM. For those that understand military time, you can avoid this step by entering times using the format: 1345 which means 1:45 p.m. You don't need to press the pound key after entering the four-digit time for delivery of your reminder. Keep in mind that you cannot schedule a reminder for delivery in the first five minutes after midnight. Other times "in the midnight hour" should be entered in the format: 0045 which means 12:45 a.m. Keep in mind that reminder times always must be at least 5 minutes in the future. Finally, you cannot schedule two reminders for the exact same date and time for delivery to the same phone number. Once you enter a delivery time, the system will play back both the date and time for the reminder as a precaution. Press 1 to accept your entries and store the reminder to disk.


    Where Reminders Are Stored. There are actually two files that make up each reminder: the .call file which places the actual call and the .gsm file which is the reminder message itself. The file naming convention is HourMinute.Date.PhoneNumber with either a .call or .gsm extension. The sound files are all stored in /var/lib/asterisk/sounds/custom. Prior to the delivery date of the reminder message, the .call file is stored in /var/spool/asterisk/reminders. Then, at midnight on the date the reminder is scheduled for delivery, the run_reminders script in /var/lib/asterisk/agi-bin moves the affected .call files to /var/spool/asterisk/wakeups. The .call files in the wakeups directory are reviewed every minute of the day by the run_wakeups script which is stored in /var/lib/asterisk/agi-bin. By examining the first four digits of the filename, the scripts look for a match with the current hour and minute of the day. Once the time for the call arrives, the .call file is moved to /var/spool/asterisk/outgoing where the Asterisk system itself processes the .call script and places the call. All dialing retries are handled internally by Asterisk with no user or program control so it's important to set your default values correctly in the reminder.php script as explained above. Once the .call file is processed, Asterisk discards the file whether the call was successful or not. As noted above, the reminder message file is only discarded if the call is completed successfully. So, from time to time, you do need to review the contents of /var/lib/asterisk/sounds/custom and discard reminder messages, if any, with dates in the past.

    Just another word of caution about the reminder message files: be very careful in deleting these files. The message files and .call files are linked by filename only, and there is no error detection or correction if the message file gets discarded before the time for the reminder call arrives. What would happen in such a situation is the call would be placed, someone would answer, Allison would say "please hold for an important reminder," and then there would be a brief silence followed by Allison saying "to repeat this reminder, press 1; otherwise, press 2" which is not entirely helpful.

    Reminder and Wakeup Call Processing. It has been documented that flooding Asterisk with a bunch of .call scripts simultaneously can cause some of the scripts to be discarded without being executed. To avoid this problem, the run_wakeups script (which handles both wakeup calls and reminder message delivery) inserts a 5-second pause after each .call file is moved to the Asterisk queue. Should you experience dropped calls, one solution would be to increase the 5-second delay to a larger number, but we would caution against doing so unless you experience delivery problems with either your reminder messages or wakeup calls.

    When you're first getting started with the reminder system, it's probably a good idea to fire up Asterisk's Command Line Interface (CLI): asterisk -r. Then you can watch as the reminders are scheduled and reminder calls are placed. Just schedule a reminder for five minutes from the time you begin the reminder call, and you'll be all set.

    For Programmers Only. If you're just getting into PHP and AGI programming with Asterisk, then have a second look at reminder.php. In particular, take a look at the section of code that begins with parse agi headers into array. As best we can tell, this is the first version of this subroutine written in PHP that actually works. If you review the log file (reminder.txt), you will see a listing of all the AGI headers which are passed by Asterisk to PHP. But this is the first code we've seen that correctly reads the headers into variables where you can actually retrieve the content. Call it a feature! For example, the commented out line ($tmp = $agi['dnid']) shows the syntax to retrieve the DNID value from Asterisk. Just make a mental note that the parse AGI headers code in reminder.php actually works. Some of our previous code inherited the mistakes of others, but we've now taken the time to get this functioning properly because we needed it for something else. Here's the complete list of AGI headers that can be saved to variables in your PHP code should the need ever arise:

    read: agi_request: reminder.php
    read: agi_channel: SIP/204-6a1a
    read: agi_language: en
    read: agi_type: SIP
    read: agi_uniqueid: 1138010325.1367
    read: agi_callerid: "Line2" <204>
    read: agi_dnid: 123
    read: agi_rdnis: unknown
    read: agi_context: reminder9
    read: agi_extension: h
    read: agi_priority: 2
    read: agi_enhanced: 0.0
    read: agi_accountcode:

    You'll also want to take note of a little quirk in Asterisk (compared to some PBXs). To decipher the extension which actually placed a call, you must parse the agi_channel variable for the data between the slash and hyphen characters since the DNID (dialed number identifier) returns the extension being called (as opposed to the originating extension) when an internal call is placed. Here's one PHP approach to get the answer which in this case happens to be extension 204. Regex wizards could probably save a line of code, but who cares.

    $CallingID = substr(stristr($agi['channel'],"/"),1);
    $CallingID = substr($CallingID,0,strrpos($CallingID,"-"));

    Wish List? Well, that about covers version 1 of our shiny new Reminder System for Asterisk. If you have enhancement suggestions or other recommendations for version 2, we'd love to hear from you. Just post a comment to this topic, and we'll have a look. In the meantime, enjoy!

    Other Asterisk Projects? For a complete catalog of all our previous Asterisk projects, click here. For the most recent articles including those you missed over the Christmas and New Year's holidays, click here and scroll down the page.

    Deploying Voice Over Wi-Fi with Asterisk

    EZgo PC

    Getting Wi-Fi to play nice is another one of those grammatically incorrect, but thorny Linux problems that Asterisk® inherits because of its roots. In the Windows and Mac worlds, we've become accustomed to plug-and-play for things like Wi-Fi USB sticks, but it ain't that easy with Linux unfortunately. Once you get the right device, this project will take you less than 30 minutes to complete. But we've invested almost a week getting everything to work ... so you won't have to. If the Linux community ever wants to see Linux used as a desktop PC, this piece of the puzzle needs some work! Even with the correct hardware and drivers, there is virtually nothing available on either the net or in the 1,000 page Linux tomes that makes this project simple. Admittedly, we're anything but Linux gurus, but we can read. And it shouldn't be this hard to find the answer to the following simple question: how do I set up a WiFi connection on my Linux box. Now that we know how, we would hasten to add that you shouldn't have to bury code in numerous obscure places in the operating system and issue dozens of unintelligible Linux commands to make a Wi-Fi adapter come to life! So, why do it, you might be asking. Keep reading, and we'll get to that.

    Who needs it? Anyone in the emergency preparedness and continuity of operations business oughta have a WiFi PBX in their suitcase! Can you spell K-A-T-R-I-N-A. And if you or your organization wants the flexibility of an instant, high performance telephone system on a moment's notice, then WiFi is a must-have. Ever been to a convention with your entire office but didn't want to leave all your phones behind? Now you don't have to. Using Wi-Fi IP phones, this entire phone system can be deployed WITH NO WIRES using a battery-operated notebook computer or, better yet, a small-footprint PC (see inset) plugged in to a $200 generator or a low-cost UPS if electricity is flowing. And someday we may get Asterisk running on our Nokia 770, but not this year.

    What to Use. The easiest solution for Voice Over Wi-Fi is a WiFi USB stick and an Asterisk@Home server. Why USB? Because you can use it with almost any modern PC on the planet without having to install a card in the machine. There are now close to two dozen different WiFi sticks available at stores such as CompUSA, MicroCenter, and Fry's. The only problem is the scarcity of Linux drivers for ALL of them. Luckily, there is one chipset that works without a dog-slow Windows driver emulator (such as ndiswrapper): the ZD1211 chipset. Both the Safecom SWLU-5400 and SWLUZ-5400 have this chipset. The real beauty of this USB adapter is that it can function as a host in an Ad Hoc Wi-Fi network, or it can be configured in Infrastructure Mode to take advantage of an existing Wi-Fi access point serving as a Master. The device also can be configured as a Master or Repeater and supports both WEP and WPA encryption as well as 802.11B and G. Bottom line: This WiFi stick has the flexibility to function in virtually any type of existing or newly created Linux Wi-Fi network. That was our theory anyway. Read on.

    Where to find one. Finding the right product turned out to be the easy part. If you're in the U.S, the more difficult task is finding a USB adapter with this chipset. In Europe, they're a dime a dozen. While they are FCC approved and include a chipset manufactured by Texas Instruments, they're almost impossible to find stateside. One possible source is eBay. Search for Safecom USB WiFi and be sure to check the Search title and description box. There's almost always at least one for sale. If not, check back in a day or two or request an email when one is listed. With shipping, you should be able to find one from various U.K. distributors for about $30. Be sure the ad states that the product uses the ZD1211 chipset, or you're wasting your money. NEWS FLASH: IOgear now makes the GWU523 which reportedly has the same chipset (although we have not actually tested one). Here's the PriceGrabber link of sources. One that we now have tested with the same chipset is AirLink's AWLL3026 which is on sale this week at Fry's or Outpost.com. If you get in a crunch and can't find one, contact us. We have a few spares (not on sale) that were used only to verify that they worked.


    Wi-Fi Deployment Issues. There are many ways to deploy a Wi-Fi network and adding an Asterisk PBX to the mix just multiplies the number of available choices. To keep things simple, we've chosen what we believe is the easiest approach, but your needs may vary so we'll point out other options as we go along. We're assuming you have an Asterisk@Home 2.2 server already running because it's the easiest way to deploy Asterisk on the Linux platform. CentOS/4 is built right in. Here's our 90-minute step-by-step guide if you need it. We're also assuming you have a WiFi access point or router (802.11B or 802.11G) already in place and working with at least one wireless device. If not, start there and come back.

    Our Wi-Fi lab (aka home) and test server for this project may or may not be typical. The PC is an EZgo which is roughly the size of a Mac mini or about 4 stacked CD cases (see inset above). You can see the USB stick hanging out the back of it. It's only about 2 inches long. And, yes, I hear someone asking, "Why not just use a Mac mini?" The short answer is that, while Asterisk runs on the Mac, it's no walk in the park. And, by the time you build a Mac system with the tools bundled in Asterisk@Home and get them all talking to each other, even my kids may be out of college. Let's just say you will quickly come to appreciate the real beauty of Asterisk@Home. For another painful approach to building a small-footprint Asterisk system, check out Tom's Networking. No, thanks.

    We have a fairly large house so we have a WiFi router at one end and a Wi-Fi access point at the other. The two devices are hard-wired together with a 100 megabit, CAT5 network cable so they're both on the same network segment: 192.168.0.1 through 192.168.0.254. Several dozen devices are scattered around the various rooms on the three levels of the house to keep me entertained but mostly to keep me out of the Little Mrs. hair. A burglar's paradise, you might be saying ... well, yes, except we'd get your picture on the way in and out.

    The ESSID on both WiFi Masters is the same: MundyLANd. You need this setup for moving around a large WiFi network. The trick is that one unit operates on WiFi channel 1 while the other is set to channel 11. That way the signals don't step on one another, and your client device has the smarts to transparently switch from one access point to the other when it needs to... much like cellphones moving between cellphone towers. For purposes of this article, we will assume that your devices operate with 128-bit WEP encryption since it's the easiest to configure. Note: We didn't say WEP was secure. We just said it was easy to configure.

    In the middle of the house, we have a new addition from Santa, a Sony wireless TV that operates as a Master on WiFi channel 6. We'll have more to say about it in a future column. And, yes, we still hate Sony after their rootkit fiasco! But Santa didn't know any better, and who are we to turn down free toys. Why is it that Sony never acted this way until an American took over the top job at the company? I'm sure our European and Asian readers will have a quick answer.

    For now, what you need to know is that our home maxes out ALL of the existing U.S. WiFi channels, much to the chagrin of our closest neighbors. Because of the overlap between Wi-Fi channels, the only practical channels to deploy are 1, 6, and 11. This becomes important for this project because we want to make sure the new Linux Wi-Fi dongle plays nicely by not using an intermediate channel or, worse yet, the Sony WiFi channel (6) which doesn't provide access to the Internet without knowing a different ESSID and password, both of which are about as long as your arm.


    The reason for this lengthy diatribe was to lay the groundwork to get you thinking Wi-Fi, and it also exposed the only technical wrinkle we observed in this project. When configured in Managed mode, the Safecom USB WiFi adapter is supposed to seek out the nearest access point and set itself to that channel. It doesn't. Our experience using this Linux driver with CentOS/4 which is bundled with Asterisk@Home 2.2 has been that this rarely works as it should. The channel selection appears to be almost totally random. When the device sets itself up on the wrong channel, all sorts of ugly things can happen. For openers, you probably won't get the network connectivity you expect. Many of the Macs and PCs in our house couldn't talk to the WiFi-enabled Asterisk box until we got the adapter correctly set to the channel of the nearest WiFi Master, channel 1 in our case. Obviously, if the device configured itself for Channel 6 or a nearby channel, we were out of luck ever talking to the box. That turned out to be a headache, but we'll show you a little discovery we made that makes it less painful. Just be aware that this may be a manual drill each time you reboot the WiFi Asterisk server or move it (not often, if you're smart!). We've had much worse luck with Windows PCs and WiFi devices over the years, so this really wasn't a deal breaker in our book. Connectivity works. The Linux driver works. Encryption works. And the phones ring. Those were the critical pieces in this puzzle from our perspective. Now on with the countdown!

    Downloading the Linux WiFi Driver. After obtaining the hardware device, the next step is finding a Linux driver for it. There happen to be two, and someday when our hair grows back, we may try the other one. In the meantime, you'll need the Linux zd1211 driver which is available from SoureForge. Just find the site closest to you and download the compressed tarball from here. Here's where you'll find the other one just in case someone else enjoys water torture. Wasn't that easy? Well, not so fast.

    Once you unzip and untar the driver bundle (tar -zxvf zd1211-XXXX.tar.gz), you'll find a README file which is actually pretty good. It only leaves out one little thing: the device name of the WiFi dongle on your Linux box. The answer is wlan0 (that's a zero on the end) so write it down. You're going to need it ... a lot!


    Compiling and Installing the WiFi Driver. Now we're getting to the fun part. We're assuming you will be installing this on a freshly installed Asterisk@Home 2.2 system built using the ISO image. You can read all about how to do that here. Will it work with another version of Asterisk@Home? Maybe. Try it. Heh heh! This isn't the Windows or Mac world so drivers have to be compiled for the Linux box on which you wish to run the software using the Linux kernel that was in place when you compiled the software. So, here's gotcha #1. If you ever get an updated kernel with CentOS/4 (as you will when you run yum -y update after installing Asterisk@Home 2.2), you will need to repeat this drill and recompile and reinstall the driver. HINT: Run yum -y update on your Asterisk@Home 2.2 server before continuing! Now you see why Linux folks aren't overly enthusiastic about kernel upgrades.

    To begin the WiFi driver install, just rename the downloaded driver folder from SourceForge to zd1211 and copy it to a safe place on your Linux machine while logged in as root, e.g. /root works great. Now switch to that folder (cd /root/zd1211) and issue the following commands to compile and install the driver:

    make
    make install

    Once this finishes, you'll need to manually load the driver. The README has some pretty good hints about this, but no cigar. Finding the correct answer requires that you first know the question to ask: find / -name *zd1211*.ko. This tells us where to find the little cream puff we want to load. Keep in mind that the "answer" will be different each time the Linux kernel version changes. If you're not running 2.6.9-22.0.1.EL, then this won't work. So how do you know which kernel you're running? Just type this simple (but obscure) command: uname -a. Now let's load the USB driver:

    insmod /lib/modules/2.6.9-22.0.1.EL/net/zd1211_mod.ko

    Once you've loaded the driver, we need to be sure it really, really is loaded: lsmod | more. Just scroll down the list with the enter key until you see an entry that starts with zd1211. Then press q to quit lsmod.

    zd1211_mod 191876 0

    Are we there yet? Why no, we're just getting started. Isn't Linux fun!

    Firing Up the Wi-Fi Adapter. Actually, we're closer to being finished than you may have thought. There are just a couple more steps. We want to manually make sure things are working. Then we'll need to configure Linux to automatically load the WiFi driver each time you reboot your system. You'll find it's easier to get the kinks out of network drivers by first doing things manually, and then recording what you've done for posterity. For openers, you'll need an IP address for this WiFi adapter. That address obviously needs to be on the same subnet as the access point with which it will be communicating. For purposes of this example, we've chosen 192.168.0.77 on the subnet of our WiFi router which has IP address 192.168.0.1. YMMV. You also need to know some other things such as the subnet mask, the router's IP address, and an IP address for your DNS (name) server, but we'll get to that. Once you've chosen an IP address, let's fire up the adapter and then check to be sure it's running:

    ifconfig wlan0 192.168.0.77
    ifconfig | more

    You should get a response that looks something like this:

    lo Link encap:Local Loopback
    inet addr:127.0.0.1 Mask:255.0.0.0
    inet6 addr: ::1/128 Scope:Host
    UP LOOPBACK RUNNING MTU:16436 Metric:1
    RX packets:12146 errors:0 dropped:0 overruns:0 frame:0
    TX packets:12146 errors:0 dropped:0 overruns:0 carrier:0
    collisions:0 txqueuelen:0
    RX bytes:985362 (962.2 KiB) TX bytes:985362 (962.2 KiB)

    wlan0 Link encap:Ethernet HWaddr 00:E0:98:F2:BB:40
    inet addr:192.168.0.77 Bcast:192.168.0.255 Mask:255.255.255.0
    inet6 addr: fe80::2e0:98ff:fef2:bb40/64 Scope:Link
    UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
    RX packets:10638 errors:0 dropped:0 overruns:0 frame:0
    TX packets:3483 errors:0 dropped:0 overruns:0 carrier:0
    collisions:0 txqueuelen:1000
    RX bytes:1301983 (1.2 MiB) TX bytes:335569 (327.7 KiB)

    You'll see that wlan0 is in the list, and it has the IP address that we assigned. That's good news. You'll also note that there is no eth0 device shown even though our machine has an Ethernet adapter built into it. This is another little gotcha to keep in mind. Make sure, if your box has an internal Ethernet adapter, that you configure the main adapter to use DHCP to obtain its address. You do this by issuing the command netconfig and using the space bar to choose Use Dynamic IP Configuration. Then tab to OK and press Enter. The reason for this is very important. Lots of Linux software makes the assumption that eth0 is the default network adapter if it happens to be running. So we want to make sure it isn't running by unplugging the Ethernet cable before booting the machine. If you have a fixed IP address for eth0, then it will load eth0 whether a cable is connected or not. If you configure eth0 for DHCP, then eth0 will not be loaded unless an IP address can be obtained from a DHCP server. With the cable unplugged, you're pretty safe. So, if your machine is showing an eth0 device, reconfigure eth0 for DHCP, and reboot the machine. Then reissue the ifconfig command after logging back in as root. When your display shows two network devices like ours (loopback and wlan0), you're ready to proceed. Another clue: When Asterisk@Home starts up and you log in to your server, it normally tells you the IP address for accessing the Asterisk Management Portal using a web browser. This gets broken when you're using a Wi-Fi adapter that isn't on eth0 because Andrew's setup is merely regurgitating the IP address of eth0. Don't worry! It still works when you enter the correct IP address of your WiFi adapter.


    Configuring the WiFi Adapter for Your Wi-Fi Network. Now that we have the WiFi adapter running, we'd like it to communicate with the WiFi network in our home or office. You obviously have to have a WiFi network up and running in your home or office for this to work. But we'll assume you've taken care of that little detail. 25+ years in the technology business has taught me to restate the obvious once in a while just in case something isn't quite as obvious to others. What we'll need now is the name of your WiFi network, the ESSID. And we need to know the channel on which your nearest WiFi access point is broadcasting. Finally, we need a way to identify this access point if you have more than one. Lucky for us, there's a Linux command that will tell us everything except your WiFi WEP password if you're using WEP encryption on your wireless LAN (you are using some sort of encryption, aren't you?).

    iwlist wlan0 scanning

    You'll get a result that looks something like the following:

    wlan0 Scan completed :
    Cell 01 - Address: 00:06:25:55:C4:E3
    ESSID:"MundyLANd"
    Mode:Master
    Frequency:2.412GHz (Channel 1)
    Quality:68/92 Signal level=54/154 Noise level=0/154
    Encryption key:on
    Bit Rate:1Mb/s
    Bit Rate:2Mb/s
    Bit Rate:5.5Mb/s
    Bit Rate:11Mb/s

    Cell 02 - Address: 00:01:4A:10:EE:60
    ESSID:"LF-X1U.00014A10EE60"
    Mode:Master
    Frequency:2.437GHz (Channel 6)
    Quality:4/92 Signal level=35/154 Noise level=0/154
    Encryption key:on
    Bit Rate:1Mb/s
    Bit Rate:2Mb/s
    Bit Rate:5.5Mb/s
    Bit Rate:11Mb/s
    Bit Rate:6Mb/s
    Bit Rate:9Mb/s
    Bit Rate:12Mb/s
    Bit Rate:18Mb/s
    Bit Rate:24Mb/s
    Bit Rate:36Mb/s
    Bit Rate:48Mb/s
    Bit Rate:54Mb/s

    Cell 03 - Address: 00:09:5B:2A:A2:AC
    ESSID:"MundyLANd"
    Mode:Master
    Frequency:2.462GHz (Channel 11)
    Quality:4/92 Signal level=36/154 Noise level=0/154
    Encryption key:on
    Bit Rate:1Mb/s
    Bit Rate:2Mb/s
    Bit Rate:5.5Mb/s
    Bit Rate:11Mb/s

    Here's where things get a little buggy. You're supposed to be able to set the the ESSID, the broadcast Mode, the access point and the WiFi channel for the zd1211 using the Linux iwconfig tool. As mentioned previously, in standard Managed mode (meaning you'll be connecting to an Access Point on your WiFi network), you have no control over the channel or the access point. The theory was that the adapter would set itself automatically. Unfortunately it doesn't although your mileage may vary if you have only a single access point within range of your zd1211.

    As we indicated on the front end, our WiFi network is somewhat atypical. So the best solution for you is to execute the following commands and see what happens on your own wireless LAN:

    ifconfig wlan0 192.168.0.77 (use your designated IP address)
    iwconfig mode Auto
    iwconfig wlan0 essid "MundyLANd" (use your ESSID)
    iwconfig wlan0 key F57EC678F1B061BAC59EC593EFE (use your WEP key)
    iwconfig mode Managed
    iwlist wlan0 scanning (reports the cell numbers of each access point in range)
    iwpriv wlan0 connect 01 (use the cell number of the access point closest to your zd1211)
    ifconfig
    iwconfig
    iwlist freq

    Now go to another machine in your network and see if you can ping your WiFi Asterisk@Home box:

    ping 192.168.0.77 (use the IP address of your WiFi AAH machine)

    If you're successful, great! Reboot your WiFi AAH machine and repeat the test. If it works twice, you're probably safe skipping the next section.


    Forcing the WiFi Adapter to a Specific Access Point and Channel. If you're getting random results with the above approach, then here's the solution that works. Unfortunately, it requires a set of eyeballs each time you reboot your system unless there's only one access point within range of your Asterisk Wi-Fi box:

    ifconfig wlan0 192.168.0.77 (use your designated IP address)
    iwconfig mode Managed
    iwconfig wlan0 essid "MundyLANd" (use your ESSID)
    iwconfig wlan0 key F57EC678F1B061BAC59EC593EFE (use your WEP key)
    iwlist wlan0 scanning (reports the cell numbers of each access point in range)
    iwpriv wlan0 connect 01 (use the cell number of the access point closest to your zd1211)
    iwconfig (should show the MAC address of the access point from step above)
    iwlist freq (should show the channel in use that matches your access point's broadcast channel)

    To be sure you have connectivity on your local area network, issue this command: ping 192.168.0.1 using the correct internal IP address of your own router/firewall.

    This setup should work every time except you have to manually look at the results of the scanning report to decipher the proper cell number because the order of the list can change each time you run it. If only one access point is reported, you're in luck! Just test it several times to be sure. Once you're sure, you should be safe in executing the iwlist wlan0 scanning command followed by iwpriv wlan0 connect 01. Don't skip the scanning step, however, as this appears to "prime the pump." One other word of warning: don't execute the scanning command without first assigning an IP address to wlan0 using ifconfig, or you'll get an unrecoverable kernel panic.

    That about covers all the gotcha's. The important lesson here is to get the network connectivity stable before you automate the process so that it autoloads when you boot your system. If you can't get stability in the channel and access point connectivity, at least make certain that you understand the limitations of your solution before making things automatic.

    Adding Internet Connectivity. We haven't mentioned getting out to the Internet yet. For that to work, you'll need access to a name server (aka DNS). Here's another little Linux "feature" to bite you. The interactive mode of netconfig is designed to handle the eth0 interface. To get things working on the wlan0 interface, issue a command like this:

    netconfig --gateway=192.168.0.1 --ip=192.168.0.77 --nameserver=68.87.68.162 --netmask=255.255.255.0 --device=wlan0

    Be sure you adjust gateway (IP address of your router); ip (IP address of your new Asterisk box); nameserver (from a Mac or Windows machine: here's how to find it; on a Linux machine: nslookup mundy.org returns your DNS server's IP address on first line following Server:; netmask (you can find this with iwconfig); device=wlan0 (leave this alone). By the way, you only have to execute this command once, not every time you boot your system.

    To make sure you've got Internet connectivity now, issue this command: ping novell.com. You should get a response showing the IP address of Novell's server. If not, check your entries above and try again.


    Activating WiFi on Startup. One last piece and we're finished. We obviously want to activate WiFi connectivity on bootup and, if you only have one WiFi access point and no neighbors like me, you shouldn't have to manually tweak your system thereafter. As we stated initially, this ain't your daddy's Windows or Mac machine, so where to put what isn't quite obvious. You're also going to need to do this in two boot cycles because there are two separate pieces. If the second piece works and the first one doesn't, you'll get a seg fault and crash every time. Do you need to ask how I know?

    Step 1: While logged in as root, create the following new file: nano -w /etc/sysconfig/network-scripts/ifcfg-wlan0 using your IP address, netmask, and gateway settings from the netconfig drill above:

    DEVICE=wlan0
    ONBOOT=yes
    BOOTPROTO=static
    IPADDR=192.168.0.77
    NETMASK=255.255.255.0
    GATEWAY=192.168.0.1

    When you're finished adding the above commands, save the new file: Ctrl-X, Y, the press Enter. Now reboot your server: shutdown -r now. Once the system comes back up, log in as root, and issue the following command: ifconfig. Make sure that the display shows an IP address (inet addr) for your wlan0:

    lo Link encap:Local Loopback
    inet addr:127.0.0.1 Mask:255.0.0.0
    inet6 addr: ::1/128 Scope:Host
    UP LOOPBACK RUNNING MTU:16436 Metric:1
    RX packets:425 errors:0 dropped:0 overruns:0 frame:0
    TX packets:425 errors:0 dropped:0 overruns:0 carrier:0
    collisions:0 txqueuelen:0
    RX bytes:32582 (31.8 KiB) TX bytes:32582 (31.8 KiB)

    wlan0 Link encap:Ethernet HWaddr 00:E0:98:F2:BB:40
    inet addr:192.168.0.77 Bcast:192.168.0.255 Mask:255.255.255.0
    inet6 addr: fe80::2e0:98ff:fef2:bb40/64 Scope:Link
    UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
    RX packets:976 errors:0 dropped:0 overruns:0 frame:0
    TX packets:592 errors:0 dropped:0 overruns:0 carrier:0
    collisions:0 txqueuelen:1000
    RX bytes:91331 (89.1 KiB) TX bytes:75930 (74.1 KiB)

    Step 2: While logged in as root, edit the following file: nano -w /etc/rc.d/rc.local. The existing file should look like the following. You'll note that this is where Asterisk@Home is actually started on bootup so be careful with your typing.

    #!/bin/sh
    #
    # This script will be executed *after* all the other init scripts.
    # You can put your own initialization stuff in here if you don't
    # want to do the full Sys V style init stuff.

    touch /var/lock/subsys/local
    /usr/local/sbin/motd.sh > /etc/motd
    /usr/sbin/amportal start

    Above the touch line, add a couple of blank lines and then cut-and-paste the following code using your ESSID and the password of your wireless LAN:

    /sbin/iwconfig wlan0 mode Auto > /dev/null
    /sbin/iwconfig wlan0 essid "MundyLANd" > /dev/null
    /sbin/iwconfig wlan0 key F57EC678F1B061BAC59EC593EFE > /dev/null
    /sbin/iwconfig wlan0 mode Managed > /dev/null
    /sbin/iwlist wlan0 scanning > /dev/null
    /sbin/iwpriv wlan0 connect 01 > /dev/null

    Leave the mode Auto line alone. On the essid line, enter the name of your WiFi LAN in quotes. If you're using WEP encryption on your wireless LAN, enter the hex key in the next line. Otherwise, just delete the line: Ctrl-K. Leave the mode Managed line as is. Leave the iwlist scanning and iwpriv lines the way they are. Now save your changes: Ctrl-X, Y, then press Enter. Reboot again: shutdown -r now. Log in as root. Then issue the following commands:

    iwconfig
    iwlist freq

    The iwconfig command will tell you if you're connected to your Access Point and also indicate the Frequency (which tells you the channel your zd1211 is on). The iwlist freq decodes frequencies into channels and tells you which one you're talking on. If it's the same as the default channel on your access point, you're done. If it's not, run the command iwlist wlan0 scanning command again and eyeball the cell number which matches your access point. Then reset the channel by issuing the following command with the cell number you deciphered instead of 01: iwpriv wlan0 connect 01. Whew! Congratulations! You're now the proud owner of an Asterisk-based Voice Over Wi-Fi PBX, and you're getting darn close to being a Linux guru. Enjoy!

    Free U.S. Calls. Everybody loves free samples so here's one for you. You may remember Gene Willingham and the good folks at TelaSIP, the winner and least expensive provider in our unlimited U.S. long distance calling shootout. In a moment of weakness, TelaSIP's agreed to let you try out some free U.S. phone calls on their nickel. The service uses our Asterisk server and TelaSIP's long distance bandwidth. Calls are limited to 10 minutes after which the callee will hear "Goodbye" followed by a click. You'll get two beeps followed by a fast busy. In other words, time's up! Here's how to use this free service. Call from a phone in the U.S. with CallerID. Call the number shown on the map (inset) which is one of our DIDs in Charleston, South Carolina. The call will never be answered so you won't be billed for the long distance call. Within 10 seconds, you should hear a fast busy. Hang up, and our Asterisk server will call you back within 30 seconds assuming you have caller ID enabled. You won't believe how many calls we receive each day from folks with caller ID disabled. HINT: No return call = No caller ID. If you do get a call back, listen carefully when you answer! You'll be provided a random password for your call, and then you'll be prompted to key it in. With your phone keypad, do that and press the pound key (#). If you get a message that it's incorrect, try again (HINT: We told you to listen carefully). Once you successfully enter the password, you'll then get a DISA dial tone. Dial 1 and then the area code and phone number of someone you love (in the United States only). This is an excellent way for you to check out the voice quality of TelaSIP calls without spending a dime. Just don't abuse the offer or this paragraph may magically disappear ... as will the free calls. For those that don't know us, we don't record your calls, and we don't store the number of the person you're calling although the Asterisk logs probably have it for a while anyway. For security purposes, we do log your CallerID and the time of your call just in case you do something you shouldn't be doing and the FBI traces the call back to us. If any of the above offends you, exercise your constitutional right to not use this free service. Believe it or not, someone wrote and asked why they couldn't use this service without CallerID enabled on their phone. Think about it! And you won't believe how many calls we receive each day with a CallerID of Asterisk. That won't work either.

    Nerd Vittles Allison Fund. As we mentioned last week, our next major project is an Appointment Reminder System. Using a touchtone phone, you can schedule an unlimited number of customized voice reminders for delivery at any future time to any phone numbers you desire. To make it work, we need customized voice prompts for the IVR interface. Unfortunately, these cost money. Allison, who records the Asterisk voice prompts for Digium, needs to eat. And so do we. We're springing for the costs of the prompts up front, but this is your golden opportunity to contribute to the cause. Provided we raise enough money to cover the costs, we'll put the voice prompts in the public domain. Otherwise, when the project is presented, those that didn't contribute will have to record their own voice prompts in order to use the system. The generous will get the Allison prompts either on a password-protected web site or by email.

    Last week's donations were a little disappointing. We raised a whopping $15 which means less than half of our regular readers have donated to the cause. And, until yesterday, there wasn't a single donor from the U.S. Thanks, Hollywood, for breaking the curse. Unless there's a surge of enthusiasm and generosity this week, better get those baritone voices tuned up. But, it's not too late to show your true colors! Go for broke and make a $5 non-tax-deductible contribution via PayPal using any major credit card. You'll sleep better! Just click on this link. And, you have our absolute commitment. If we hit a million bucks, we'll never pass the hat again. Of course, we might stop writing these silly articles, too. But, based upon last week's numbers, it doesn't look like there's much risk of a financial windfall here. We're just trying to recoup the costs of the voice prompts.

    Coming Attractions. We have a number of projects underway to keep us busy for the next few weeks and months, but we always love to hear from you. If there is a particular Asterisk project you'd like us to cover down the road, leave a comment. We read every one of them. On our short list at the moment are a HOW-TO on connecting two Asterisk servers together and all the great things you can do once you've done that such as building a failover Asterisk system, HOW-TO connect remote IP and IAX phones to your Asterisk system (both wired and WiFi), instant messaging, key system emulation with Asterisk@Home 2.2, DUNDI, HOW-TO let Asterisk read your emails to you over the phone, the new Appointment Reminder System mentioned above, plus a few surprises. Don't expect all of this overnight, but we'll get there. With most of this stuff, we usually write about it as we build it. So you're reading these articles while riding in the front seat most of the time. Come back and visit often and tell your friends about us.


    Some Recent Nerd Vittles Articles of Interest...

    Introducing TeleYapper: Free Asterisk Message Broadcasting System, Part II

    It's free software day again at Nerd Vittles, and today it's Part II of TeleYapper, an Asterisk@Home-based telephone broadcasting service for neighborhoods, schools, little leagues, fundraisers, municipal governments, and anyone else that just wants to pester folks with annoying, but free, prerecorded phone calls. Everything you'll need to get TeleYapper working is in this article, so there's no need to begin with Part I unless you just want to. TeleYapper works like this. You create a recorded message using Asterisk®. Then you create a list of phone numbers to call in a MySQL database using a tool such as phpMyAdmin which is bundled with Asterisk@Home. Finally, you place a phone call either to kick off TeleYapper or to redial calls that failed the first time around. The software will dutifully swing into action and call qualifying phone numbers from any of ten calling categories that you specify when you set up your database of callees. TeleYapper then will deliver the message you've recorded. It works much like call-em-all.com and numerous other telephone broadcasting services with one important difference: TeleYapper is FREE! So, instead of paying 15¢ a call or $35 to $100 a month for a commercial service or spending thousands of dollars for a commercial dialer, now you can do it yourself using TeleYapper and your (also free) Asterisk@Home 1.5 or 2.2 PBX. Today we'll actually get TeleYapper making calls and emailing you the results of those calls. Don't be intimidated by the length of the article. You can still complete this project in about 30 minutes. It's mostly a cut-and-paste exercise. We've done all the hard work for you. NOTE: For those using Asterisk@Home 2.4 or later, use this updated tutorial and software.

    Legalese. For those that are used to buying flawless software such as Microsoft Windows or Microsoft Office, let's be sure we're all on the same page up front. First, you're not buying this software. It's FREE! And, yes, sometimes you get what you pay for. Second, don't assume today's version is error-free. It's probably not. We try pretty hard to write reliable code, but even the best among us make mistakes. Third, by downloading or using this software, you are agreeing to assume all risks associated with use of the software. NO WARRANTIES EXPRESS OR IMPLIED INCLUDING ITS FITNESS FOR USE OR MERCHANTABILITY ARE PROVIDED WITH THIS SOFTWARE. And, finally, read or reread Part I of this series concerning Do Not Call statutes in your jurisdiction and make sure you are in compliance before placing any calls. Failure to heed this advice may subject you to serious criminal and civil penalties. If any of this gives you heartburn, exercise your constitutional right to not use the software.


    Overview. One of the reasons we continue to write software applications for Asterisk is to document for others how easy it is to create really useful software for this terrific open source platform. When we began using Asterisk, there was precious little code available with sufficient documentation to give much of a hint on how to write applications. There were lots of reasons for this not the least of which was a desire by existing Asterisk developers to protect their livelihood. We don't want to jeopardize anyone's ability to make a living, but the spread of an open source PBX with good applications will only lead to more business, not less. Today's installment provides a good framework for anyone wanting to write Asterisk AGI scripts using PHP. The code is well-documented to demonstrate how to pass variables to an AGI script from your dialplan and how to retrieve variables from an Asterisk AGI script into your dialplan. We needed this for TeleYapper because we're using a phone call to an Interactive Voice Response (IVR) session embedded in the dialplan to begin the calling process. We use the IVR session not only to determine which group of callees to call but also to give the caller the option of placing a call to everyone in the group or just those to whom the initial call was unsuccessful. After the caller hangs up, the results are passed to the teleyapper.php application to do the heavy lifting. The PHP program takes advantage of an AGI script's ability to actually set dialplan code in motion once a call is answered. In order to log calls and track which ones are successful, we have to pass variables into that dialplan code and then execute another PHP script when the call is completed. Stated another way, every call requires two round-trips from the Asterisk dialplan to PHP/AGI scripts. So, if you can't figure out how to pass variables back and forth using this application, you probably need to take up another line of work. For those that just want to use the TeleYapper application and not learn much of anything about programming, you're welcome to do that subject to the license agreement which follows. We hope you'll put it to good use for the betterment of a school, an intramural sports program, or a neighborhood in which you live.

    Creative Commons LicenseLicensing. We are retaining ownership of this software as well as the copyright. It is licensed for use under the terms of the Creative Commons Attribution Non-Commercial license. A Plain English summary is available here. We've done this primarily to do our part to stamp out the telemarketing creeps of the world. Those wishing to use TeleYapper in a commercial environment must first request a license outlining your proposed terms of use. We will promptly respond with a yay or nay. Telemarketers need not apply!

    Asterisk Version Issues. In our first installment, we were ambushed by some changes in the way the new version of Asterisk handles management of memory variables. We're trying not to fall in the same trap again. Suffice it to say, the Asterisk developers decided to change the name of the verb, and the syntax, and the functionality of storing data to memory variables. There may be a good reason for it, but we can't think of any. And we certainly can't think of a reason why you would eliminate the old way of doing things in a product that is barely a year old. Nevertheless, we're all stuck with it. The bottom line is that, so long as we try to maintain versions of our applications for both Asterisk@Home 1.5 and Asterisk@Home 2.2, you'll need to download code that is compatible with the version of Asterisk you are running.

    TeleYapper in a Nutshell. Before we get to the code, let's briefly cover how this message broadcasting system works. When you dial 674, the TeleYapper system will answer and prompt you for your password. Once you correctly enter the password, an interactive voice response (IVR) system will swing into action and give you several choices. That's what the [yapper] context handles. Pressing 1 lets you listen to your prerecorded TeleYapper message (if you have one). You don't yet so don't press 1. Pressing 2 lets you record a new TeleYapper message. This is handled by the [yapper2] context. Do this first and record something ... anything. You can rerecord a new message at any time by choosing option 2 again. Pressing 3 lets you kick off a TeleYapper dialing spree. It's handled by the [yapper3] context. Don't do this until we add your new database below, or you'll get smoke out of your system. If you choose option 3 to initiate a TeleYapper calling session, the system will first prompt you for a group option number to use. This is managed by the [yapper-options] context. Simply stated, when you build your database of callees for TeleYapper, you can specify a one-digit group number for each entry in the file. Then, when you begin a calling session, you can narrow down the calling group by telling TeleYapper which group of callees to call. If you want a callee to be in more than one group, you simply enter that callee into the database multiple times with different group numbers. If you want everyone in the same group, then enter 0 for every person in your database.


    Once you specify the group number during your TeleYapper session, the system will actually look up and report back how many messages will be delivered to the callee group you've chosen. Allison will say something like this assuming there were 146 calls to be placed: "The number I have is one hundred and forty six messages." This will give you the count of qualifying records in the database and the option of proceeding with the calls, cancelling the transaction, or just redialing the numbers of the calls that failed to this group on the previous pass through the database. As noted previously, we've endeavored to build this entire application using the voice prompts that are delivered with Asterisk@Home 1.5 or 2.x so they're not quite perfect. But they work reasonably well once you understand how the pieces fit together. You're supposed to be nodding in agreement now. If not, reread the last two paragraphs.

    TeleYapper's Calling Process. For those that like lists, it may help to visualize how all the TeleYapper code fits together by laying out the actual program steps in a typical call:

  • Caller with TeleYapper password places call to M-S-G (extension 674) to activate a TeleYapper session.
  • Asterisk answers the call, provides IVR menu: playback a message, record a new message, or place a call.
  • If caller chooses to place a call, IVR prompts for Group number to call (0-9).
  • Asterisk passes the Group number to MySQL (checkgroup.php) to look up the number of callees in the chosen Group.
  • Group count is passed back to Asterisk which uses Allison to tell the caller how many callees are in the chosen Group.
  • Caller has option of placing the call, hanging up, or choosing advanced options (which redials previously unsuccessful calls in chosen Group again).
  • If caller chooses to place a new call, Asterisk thanks the caller, hangs up, and then passes control to teleyapper.php to handle placing the calls.
  • TeleYapper creates dialing scripts a minute apart for each call beginning two minutes after the initiating request. Scripts are placed in the wakeupcall queue.
  • TeleYapper initializes the date/time and status fields for each record in the Group to be called. These are only filled in when a call is then answered.
  • If you've enabled logging in teleyapper.php, then the log is generated after all of the call setups have been completed.
  • If you've enabled emailing of the teleyapper.php log, then the log is emailed to your email address at the same time.
  • Wakeup call queue is checked every minute by a cron job which moves qualifying calls to the Asterisk outbound call queue for dialing.
  • Asterisk places each call as it hits the call queue and waits for the callee to answer.
  • If no one answers the call, nothing is posted to the MySQL database regarding call completion. That's how we identify unsuccessful calls.
  • If the call is answered, the callee is advised to hold for an important message and then your prerecorded message plays.
  • Callee is then prompted to press 1 to replay the message, press 2 to blacklist the last caller (you!), or press 3 to hang up.
  • If callee presses 1, your message is replayed, and then the call is disconnected. MySQL database will show date/time of call with ReplayedMsg as status.
  • If callee presses 2, log will reflect that caller requested blacklisting. MySQL will actually DELETE this person from your database. It's the LAW!
  • If callee presses 3, Allison says goodbye and Asterisk hangs up the call. MySQL database will show date/time of call with status of OK.
  • If callee makes no choice, Asterisk will replay your message, then hang up, and record the date/time of call with status of AnsMachine.
  • If you've enabled logging in teleyapper2.php, then the individual call log is generated and appended to the main log file after each call has been placed.
  • If you've enabled emailing in teleyapper2.php, then the call log is emailed to your email address after EACH call has been placed.
  • The TeleYapper Components. Last week we provided the beginnings of the TeleYapper application for you to experiment with. This included the MySQL database and some chunks of code for your AAH dialplan (depending upon which version of AAH you were using) plus checkgroup.php (used to compute the number of callees in the group selected by caller). Today we want to update some of the original code. Then we'll add some additional pieces of code so that the application can do something meaningful ... like calling people with your broadcast message.


    Actually, the new code not only handles the actual dialing of the callees you've entered in your MySQL database (teleyapper.php), it also plays your message when a callee answers (dialplan contexts), and documents what happened during the calls (teleyapper2.php). Call progress is documented in two ways. First, when a call is completed, TeleYapper will log the date and time of the call as well as a best guess of what happened during the call in your MySQL database. So browsing entries in your TeleYapper database will always show the date, time, and status of the last completed call to each callee. We'll build a web interface for this one of these days. Second, when you install the TeleYapper PHP components, there are some configuration options which will also let you create a detailed log of what happened during the TeleYapper calls. If you have email working reliably on your Asterisk system, you also can enter your email address and tell TeleYapper to email you every log that is produced. There are log entries for the initial call setup (handled by teleyapper.php) and for the placement of the individual calls (handled by teleyapper2.php). Finally, you have the option of creating a new log with each series of calls that are placed (the default setting), or you can configure TeleYapper to keep adding to the end of the initial log. In the latter case, it's up to you to erase the log before it fills up your disk. Individual call entries, if logged, will be appended to the main TeleYapper call setup log (/var/log/asterisk/teleyapper.txt).

    To keep things simple, everything you'll need to make TeleYapper work is covered in this article even though we covered some of it last week. Where there is overlap with last week's article, we'll tell you, and you can skip that step. It simply didn't make much sense for new visitors to have to jump back and forth between the two articles to get a working system. That's one of the limitations of the blog format unfortunately. Here are the components that make up the complete TeleYapper system, and we'll cover them below in the order which simplifies the installation process:

  • TeleYapper MySQL database
  • Code Snippet to Answer M-S-G Calls
  • AutoAttendant Contexts
  • checkgroup.php AGI script
  • Call Processing Contexts
  • teleyapper.php AGI script
  • teleyapper2.php AGI script
  • Creating the TeleYapper Database. We use Asterisk@Home's MySQL database management system to manage the list of callees for TeleYapper to dial. It can handle a database of almost any size and generally stands up well in performance comparisons with Oracle. So you're covered on the database front. If you downloaded and installed this database in Part I, you can skip this section. Nothing has changed since Part I.

    To create the MySQL database to support TeleYapper, the easiest way is using the Asterisk Management Portal (AMP) to gain access to phpMyAdmin: AMP->Maintenance->phpMyAdmin. When phpMyAdmin loads, click on the SQL icon in the left column. When the SQL window appears, clear the existing SQL query and then cut-and-paste the following SQL code into that box and then click the Go button. When the import completes, click the teleyapper.callees table entry in the left column to open the file. Then click the Insert tab at the top of the right column to add entries to the table. You only need to add information for the name, phonenum, and group fields in the corresponding values column. The id, lastokcall, and lastcall fields should be left as is. The id field gets calculated automatically. The lastokcall will record the time and date of the last successful call using TeleYapper. And the lastcall field identifies what happened during the last call to this person, e.g. ok means the call was completed successfully, no answer means no one answered the call, or answering machine means an answering machine took the call.


    You can add up to two records at a time and, by clicking the Insert Another New Row button, you will be returned to this data entry screen after you save your entries by clicking the Go button. The name field allows you to quickly review entries you've made. It won't be used when making TeleYapper calls. The phonenum field is the important one. This is the exact dial string required to place a call on your Asterisk system to this callee using whatever VoIP or PSTN outbound trunk you plan to use with TeleYapper. For example, if your preferred provider requires 11-digit phone numbers with a 1, area code, and number, then that's the way the numbers should be entered into the TeleYapper database. The group field has already been discussed. Just enter a number between 0 and 9 to identify the group with whom this individual should be associated. Finally, after adding records to the table, you can click the Browse tab to review your entries. And, while Browsing, you can click the Pencil icon beside any record entry to edit it. Clicking the red X icon beside a record entry deletes the record. If, for some reason, you wish to delete ALL the records in the file, click the Empty tab at the top of the right column. Under no circumstances should you click on the Drop tab as this removes not only the table's contents but also the table structure itself. In short, you'd have to import the database table again.

    Answering the Incoming Call. This is a simple addition to your dialplan to actually answer calls to M-S-G (extension 674) and pass them to the TeleYapper contexts for processing. If you went through Part I, you can skip this step. Nothing has changed. Using a web browser, open the Asterisk Management Portal (AMP) by entering the IP address of your Asterisk machine. To add TeleYapper to your dialplan, just cut-and-paste the following code into the [from-internal-custom] context near the top of extensions_custom.conf: AMP->Maintenance->Config Edit->extensions_custom.conf. Be sure to change the 1234 password below to something secure for your system since this will be used to gain access to your TeleYapper system!

    exten => 674,1,Answer ; dial MSG on any extension to manage your TeleYapper system
    exten => 674,2,Wait(1)
    exten => 674,3,Authenticate(1234)
    exten => 674,4,Goto(yapper,s,1)

    If you're a long-time reader of Nerd Vittles and you're using either our Stealth AutoAttendant or some other AutoAttendant, then you already know why you need to be careful about putting extensions like 6-7-4 in your extensions_custom.conf file because anyone can call you, dial 6-7-4 while your AutoAttendant is playing, and insert their own obscene message into your TeleYapper system. The solution is adding your own secure password in line 3 above rather than using the default 1234. Another precaution you should always perform is to first play your outgoing TeleYapper message to yourself to make certain it says what you think it should before you kick off a dialing spree to a thousand of your closest friends or business associates.

    AutoAttendant Contexts for TeleYapper. The simplest solution for those that read Part I of the TeleYapper series is to just block delete the [yapper], [yapper2], [yapper3], [yapper-options], and [yapper-gen] contexts at the bottom of your extensions_custom.conf file. Then, for new and old readers alike, insert one of the following chunks of code at the bottom of extensions_custom.conf depending upon your version of Asterisk@Home. Last week we actually presented three approaches. Two used checkgroup.php to retrieve the group count for the autoattendant dialogue while one(for AAH 1.5 only) actually performed the MySQL group count lookups in the dialplan itself. We've given up on the latter approach as something we want to support. The Asterisk developers barely do, and it doesn't appear to work in Asterisk@Home 2.2 (Asterisk 1.5.1) anyway. For those that are curious, we've documented how it works in Part I if you want to wrestle with it.


    For users of Asterisk@Home 1.5, open the Asterisk Management Portal (AMP) by entering the IP address of your Asterisk machine. Cut-and-paste the following six contexts into the bottom of extensions_custom.conf: AMP->Maintenance->Config Edit->extensions_custom.conf:

    [yapper]
    exten => s,1,DigitTimeout(7)
    exten => s,2,ResponseTimeout(10)
    exten => s,3,Background(T-to-hear-cur-ancmnt)
    exten => s,4,Background(press-1)
    exten => s,5,Background(to-rerecord-yr-message)
    exten => s,6,Background(press-2)
    exten => s,7,Background(to-place-outgoing-call)
    exten => s,8,Background(press-3)
    exten => s,9,Background(to-hear-menu-again)
    exten => s,10,Background(press-4)
    exten => s,11,Background(to-hang-up)
    exten => s,12,Background(press-5)
    exten => 1,1,Playback(custom/broadcast)
    exten => 1,2,Wait(2)
    exten => 1,3,Goto(s,3)
    exten => 2,1,Goto(yapper2,s,1)
    exten => 3,1,Goto(yapper-options,s,1)
    exten => 4,1,Goto(s,3)
    exten => 5,1,Playback(goodbye)
    exten => 5,2,Hangup
    exten => t,1,Goto(s,3)
    exten => i,1,Goto(s,3)
    exten => o,1,Goto(s,3)
    exten => h,1,Hangup

    [yapper2]
    exten => s,1,Playback(after-the-tone)
    exten => s,2,Playback(say-temp-msg-prs-pound)
    exten => s,3,Wait(2)
    exten => s,4,Record(custom/broadcast:gsm)
    exten => s,5,Wait(2)
    exten => s,6,Playback(custom/broadcast)
    exten => s,7,Wait(2)
    exten => s,8,Playback(your-msg-has-been-saved)
    exten => s,9,Wait(2)
    exten => s,10,Goto(yapper,s,1)

    [yapper3]
    exten => s,1,AGI(checkgroup.php|${GROUP})
    exten => s,2,NoOp(tmp variable: ${tmp})
    exten => s,3,GotoIf($[${tmp} = 0]?9,1)
    exten => s,4,SetGlobalVar(COUNTER=${tmp})
    exten => s,5,Playback(the-num-i-have-is)
    exten => s,6,SayNumber(${COUNTER})
    exten => s,7,Playback(vm-messages)
    exten => s,8,Wait(1)
    exten => s,9,DigitTimeout(7)
    exten => s,10,ResponseTimeout(10)
    exten => s,11,Background(to-call-this-number)
    exten => s,12,Background(press-1)
    exten => s,13,Background(to-hang-up)
    exten => s,14,Background(press-2)
    exten => s,15,Background(vm-advopts)
    exten => 1,1,Goto(yapper-gen,s,1)
    exten => 2,1,Playback(goodbye)
    exten => 2,2,Hangup
    exten => 3,1,Goto(yapper-redial,s,1)
    exten => 9,1,Playback(dir-nomatch)
    exten => 9,2,Wait(1)
    exten => 9,3,Goto(yapper,s,1)
    exten => t,1,Playback(goodbye)
    exten => t,2,Hangup
    exten => i,1,Playback(goodbye)
    exten => i,2,Hangup
    exten => h,1,Hangup

    [yapper-options]
    exten => s,1,Wait(1)
    exten => s,2,Playback(you-have-these-options)
    exten => s,3,Playback(digits/0)
    exten => s,4,Playback(through)
    exten => s,5,Playback(digits/9)
    exten => s,6,Playback(press-star-cancel)
    exten => s,7,SetGlobalVar(GROUP=0)
    exten => s,8,Read(GROUP,vm-enter-num-to-call,1)
    exten => s,9,NoOp(${GROUP})
    exten => s,10,GotoIf($["foo${GROUP}" = "foo"]?s,78)
    exten => s,11,GotoIf($["foo${GROUP}" = "foo*"]?s,88)
    exten => s,12,GotoIf($["foo${GROUP}" = "foo#"]?s,98)
    exten => s,13,Goto(yapper3,s,1)
    exten => s,78,Playback(connection-timed-out)
    exten => s,79,Wait(1)
    exten => s,80,Goto(yapper,s,1)
    exten => s,88,Playback(cancelled)
    exten => s,89,Wait(1)
    exten => s,90,Goto(yapper,s,1)
    exten => s,98,Playback(option-not-implemented)
    exten => s,99,Goto(yapper,s,1)

    [yapper-gen]
    exten => s,1,Playback(speed-dial)
    exten => s,2,Playback(activated)
    exten => s,3,Wait(1)
    exten => s,4,Playback(goodbye)
    exten => s,5,Hangup
    exten => h,1,DeadAGI(teleyapper.php|${GROUP}|${COUNTER}|1)
    exten => h,2,Hangup

    [yapper-redial]
    exten => s,1,Playback(speed-dial)
    exten => s,2,Playback(activated)
    exten => s,3,Wait(1)
    exten => s,4,Playback(goodbye)
    exten => s,5,Hangup
    exten => h,1,DeadAGI(teleyapper.php|${GROUP}|${COUNTER}|3)
    exten => h,2,Hangup


    For users of Asterisk@Home 2.2, open the Asterisk Management Portal (AMP) by entering the IP address of your Asterisk machine. Cut-and-paste the following six contexts into the bottom of extensions_custom.conf: AMP->Maintenance->Config Edit->extensions_custom.conf:

    [yapper]
    exten => s,1,DigitTimeout(7)
    exten => s,2,ResponseTimeout(10)
    exten => s,3,Background(T-to-hear-cur-ancmnt)
    exten => s,4,Background(press-1)
    exten => s,5,Background(to-rerecord-yr-message)
    exten => s,6,Background(press-2)
    exten => s,7,Background(to-place-outgoing-call)
    exten => s,8,Background(press-3)
    exten => s,9,Background(to-hear-menu-again)
    exten => s,10,Background(press-4)
    exten => s,11,Background(to-hang-up)
    exten => s,12,Background(press-5)
    exten => 1,1,Playback(custom/broadcast)
    exten => 1,2,Wait(2)
    exten => 1,3,Goto(s,3)
    exten => 2,1,Goto(yapper2,s,1)
    exten => 3,1,Goto(yapper-options,s,1)
    exten => 4,1,Goto(s,3)
    exten => 5,1,Playback(goodbye)
    exten => 5,2,Hangup
    exten => t,1,Goto(s,3)
    exten => i,1,Goto(s,3)
    exten => o,1,Goto(s,3)
    exten => h,1,Hangup

    [yapper2]
    exten => s,1,Playback(after-the-tone)
    exten => s,2,Playback(say-temp-msg-prs-pound)
    exten => s,3,Wait(2)
    exten => s,4,Record(custom/broadcast:gsm)
    exten => s,5,Wait(2)
    exten => s,6,Playback(custom/broadcast)
    exten => s,7,Wait(2)
    exten => s,8,Playback(your-msg-has-been-saved)
    exten => s,9,Wait(2)
    exten => s,10,Goto(yapper,s,1)

    [yapper3]
    exten => s,1,AGI(checkgroup.php|${GROUP})
    exten => s,2,NoOp(tmp variable: ${tmp})
    exten => s,3,GotoIf($[${tmp} = 0]?9,1)
    exten => s,4,Set(COUNTER=${tmp}|g)
    exten => s,5,Playback(the-num-i-have-is)
    exten => s,6,SayNumber(${COUNTER})
    exten => s,7,Playback(vm-messages)
    exten => s,8,Wait(1)
    exten => s,9,DigitTimeout(7)
    exten => s,10,ResponseTimeout(10)
    exten => s,11,Background(to-call-this-number)
    exten => s,12,Background(press-1)
    exten => s,13,Background(to-hang-up)
    exten => s,14,Background(press-2)
    exten => s,15,Background(vm-advopts)
    exten => 1,1,Goto(yapper-gen,s,1)
    exten => 2,1,Playback(goodbye)
    exten => 2,2,Hangup
    exten => 3,1,Goto(yapper-redial,s,1)
    exten => 9,1,Playback(dir-nomatch)
    exten => 9,2,Wait(1)
    exten => 9,3,Goto(yapper,s,1)
    exten => t,1,Playback(goodbye)
    exten => t,2,Hangup
    exten => i,1,Playback(goodbye)
    exten => i,2,Hangup
    exten => h,1,Hangup

    [yapper-options]
    exten => s,1,Wait(1)
    exten => s,2,Playback(you-have-these-options)
    exten => s,3,Playback(digits/0)
    exten => s,4,Playback(through)
    exten => s,5,Playback(digits/9)
    exten => s,6,Playback(press-star-cancel)
    exten => s,7,Read(tmp,vm-enter-num-to-call,1)
    exten => s,8,Set(GROUP=${tmp}|g)
    exten => s,9,NoOp(${GROUP})
    exten => s,10,GotoIf($["foo${GROUP}" = "foo"]?s,78)
    exten => s,11,GotoIf($["foo${GROUP}" = "foo*"]?s,88)
    exten => s,12,GotoIf($["foo${GROUP}" = "foo#"]?s,98)
    exten => s,13,Goto(yapper3,s,1)
    exten => s,78,Playback(connection-timed-out)
    exten => s,79,Wait(1)
    exten => s,80,Goto(yapper,s,1)
    exten => s,88,Playback(cancelled)
    exten => s,89,Wait(1)
    exten => s,90,Goto(yapper,s,1)
    exten => s,98,Playback(option-not-implemented)
    exten => s,99,Goto(yapper,s,1)

    [yapper-gen]
    exten => s,1,Playback(speed-dial)
    exten => s,2,Playback(activated)
    exten => s,3,Wait(1)
    exten => s,4,Playback(goodbye)
    exten => s,5,Hangup
    exten => h,1,DeadAGI(teleyapper.php|${GROUP}|${COUNTER}|1)
    exten => h,2,Hangup

    [yapper-redial]
    exten => s,1,Playback(speed-dial)
    exten => s,2,Playback(activated)
    exten => s,3,Wait(1)
    exten => s,4,Playback(goodbye)
    exten => s,5,Hangup
    exten => h,1,DeadAGI(teleyapper.php|${GROUP}|${COUNTER}|3)
    exten => h,2,Hangup



    Call Processing Contexts for TeleYapper. All of this code is new. For new and old readers alike, insert one of the following chunks of code at the bottom of extensions_custom.conf depending upon your version of Asterisk@Home.

    For Asterisk@Home 1.5 users, cut-and-paste the following two contexts:

    [broadcast]
    exten => s,1,Answer
    exten => s,2,Wait(2)
    exten => s,3,Playback(system-status-msg)
    exten => s,4,Wait(2)
    ;exten => s,5,BackgroundDetect(custom/broadcast|1000|50|3000)
    exten => s,5,Playback(custom/broadcast)
    exten => s,6,Goto(talk,1)
    exten => t,1,Goto(talk,1)
    exten => i,1,Goto(talk,1)
    exten => o,1,Goto(talk,1)
    exten => h,1,NoOp(Callee hung up call before menu. Dialed: ${DIAL} ID: ${ID}.)
    exten => h,2,SetGlobalVar(STATUS='EarlyHangup')
    exten => h,3,DeadAGI(teleyapper2.php|${ID}|${STATUS}|${DIAL})
    exten => h,4,Hangup
    exten => talk,1,Goto(broadcast2,s,1)

    [broadcast2]
    exten => s,1,SetGlobalVar(STATUS='Answered')
    exten => s,2,DigitTimeout(4)
    exten => s,3,ResponseTimeout(4)
    exten => s,4,Background(to-hear-msg-again)
    exten => s,5,Background(press-1)
    exten => s,6,Background(to-blklist-last-caller)
    exten => s,7,Background(digits/2)
    exten => s,8,Background(otherwise-press)
    exten => s,9,Background(digits/3)
    exten => t,1,NoOp(Callee's Answering Machine probably answered. Dialed: ${DIAL} ID: ${ID}.)
    exten => t,2,SetGlobalVar(STATUS='AnsMachine')
    exten => t,3,Background(restarting)
    exten => t,4,Wait(1)
    exten => t,5,Playback(custom/broadcast) ; playing again for ans machine
    exten => t,6,Background(goodbye)
    exten => t,7,Hangup
    exten => h,1,DeadAGI(teleyapper2.php|${ID}|${STATUS}|${DIAL})
    exten => h,2,Hangup
    exten => i,1,Goto(1,1)
    exten => o,1,Goto(1,1)
    exten => 1,1,SetGlobalVar(STATUS='ReplayedMsg')
    exten => 1,2,Goto(t,3)
    exten => 2,1,SetGlobalVar(STATUS='Zap')
    exten => 2,2,Background(num-was-successfully)
    exten => 2,3,Background(removed)
    exten => 2,4,Background(goodbye)
    exten => 2,5,NoOp(Callee Requested to have number removed. Dialed: ${DIAL} ID: ${ID}.)
    exten => 2,6,Hangup
    exten => 3,1,SetGlobalVar(STATUS='OK')
    exten => 3,2,Background(goodbye)
    exten => 3,3,NoOp(Callee Acknowledged Call. Dialed: ${DIAL} ID: ${ID}.)
    exten => 3,4,Hangup


    For Asterisk@Home 2.2 users, cut-and-paste the following two contexts:

    [broadcast]
    exten => s,1,Answer
    exten => s,2,Wait(2)
    exten => s,3,Playback(system-status-msg)
    exten => s,4,Wait(2)
    ;exten => s,5,BackgroundDetect(custom/broadcast|1000|50|3000)
    exten => s,5,Playback(custom/broadcast)
    exten => s,6,Goto(talk,1)
    exten => t,1,Goto(talk,1)
    exten => i,1,Goto(talk,1)
    exten => o,1,Goto(talk,1)
    exten => h,1,NoOp(Callee hung up call before menu. Dialed: ${DIAL} ID: ${ID}.)
    exten => h,2,Set(STATUS='EarlyHangup'|g)
    exten => h,3,DeadAGI(teleyapper2.php|${ID}|${STATUS}|${DIAL})
    exten => h,4,Hangup
    exten => talk,1,Goto(broadcast2,s,1)

    [broadcast2]
    exten => s,1,Set(STATUS='Answered'|g)
    exten => s,2,DigitTimeout(4)
    exten => s,3,ResponseTimeout(4)
    exten => s,4,Background(to-hear-msg-again)
    exten => s,5,Background(press-1)
    exten => s,6,Background(to-blklist-last-caller)
    exten => s,7,Background(digits/2)
    exten => s,8,Background(otherwise-press)
    exten => s,9,Background(digits/3)
    exten => t,1,NoOp(Callee's Answering Machine probably answered. Dialed: ${DIAL} ID: ${ID}.)
    exten => t,2,Set(STATUS='AnsMachine'|g)
    exten => t,3,Background(restarting)
    exten => t,4,Wait(1)
    exten => t,5,Playback(custom/broadcast) ; playing again for ans machine
    exten => t,6,Background(goodbye)
    exten => t,7,Hangup
    exten => h,1,DeadAGI(teleyapper2.php|${ID}|${STATUS}|${DIAL})
    exten => h,2,Hangup
    exten => i,1,Goto(1,1)
    exten => o,1,Goto(1,1)
    exten => 1,1,Set(STATUS='ReplayedMsg'|g)
    exten => 1,2,Goto(t,3)
    exten => 2,1,Set(STATUS='Zap'|g)
    exten => 2,2,Background(num-was-successfully)
    exten => 2,3,Background(removed)
    exten => 2,4,Background(goodbye)
    exten => 2,5,NoOp(Callee Requested to have number removed. Dialed: ${DIAL} ID: ${ID}.)
    exten => 2,6,Hangup
    exten => 3,1,Set(STATUS='OK'|g)
    exten => 3,2,Background(goodbye)
    exten => 3,3,NoOp(Callee Acknowledged Call. Dialed: ${DIAL} ID: ${ID}.)
    exten => 3,4,Hangup


    Once you finish adding all of the new contexts to extensions_custom.conf, click the Update button to save your changes to disk. There's no need to reload Asterisk just yet. We've still got our AGI scripts to install.

    For those that are curious, you'll notice there is a commented out line 5 in the [broadcast] context. It's an Asterisk command called BackgroundDetect. What this command is supposed to do is play a sound file while listening for silence at the callee's end of the call. Once silence is detected, the call processing drops to talk. We couldn't get it to work reliably so the current release blindly plays your message and then asks for an acknowledgment. If it doesn't get one, it plays your message again, and then hangs up. The theory here is that, even if a callee has an answering machine, the second playing of your message should get recorded. We'll see what the feedback from the pioneers reveals. Just be aware that there may be further adjustments in the coming days and weeks. So check back and read the latest comments to this blog entry.


    Installing checkgroup.php AGI Script. We introduced this script in Part I to solve a bug in Asterisk@Home 2.2 which appears to be inherited from Asterisk 1.2.1. If you've already downloaded it, you can skip this section. Nothing has changed. For those just joining us, you'll need to install the checkgroup.php script in your /var/lib/asterisk/agi-bin directory and change file ownership and permissions on the file. Log in to your Asterisk server as root, and then execute the following commands:

    cd /var/lib/asterisk/agi-bin
    wget http://nerdvittles.com/checkgroup.zip
    unzip checkgroup.zip
    rm checkgroup.zip
    chmod 775 checkgroup.php
    chown asterisk:asterisk checkgroup.php

    This script includes a debug log. The default settings are to create a new log file (/var/log/asterisk/telecheck.txt) each time the script is executed. This doesn't take up much room and is always there for you to read if something comes unglued: cat /var/log/asterisk/telecheck.txt. There are some other options. You can turn off the log file entirely ($debug=0). You can choose not to erase the previous log file each time the script is run ($newlogeachdebug=0) in which case the file continues to grow until your hard disk fills up. And you can have the log file emailed to you each time the script is executed ($emaildebuglog=1) by adding your email address ($email=youremailaddress). The last option obviously assumes you have followed our previous tutorials and gotten outbound email working reliably on your system. The functions are controlled by the following lines at the top of the checkgroup.php file. 1 means yes, and 0 means no. Just edit the file carefully: nano -w checkgroup.php. And save your changes when you're finished: Ctrl-X, Y, then press Enter.

    $debug = 1;
    $newlogeachdebug = 1;
    $emaildebuglog = 0;
    $email = "yourname@yourdomain" ;


    Installing teleyapper.php AGI Script. This code is new. New and old readers will need to install the teleyapper.php script in your /var/lib/asterisk/agi-bin directory, change file ownership and permissions on the file, and then set some configuration options. Log in to your Asterisk server as root, and then execute the following commands:

    For Asterisk@Home 1.5 users:


    cd /var/lib/asterisk/agi-bin
    wget http://nerdvittles.com/aah15/teleyapper.zip
    unzip teleyapper.zip
    rm teleyapper.zip
    chmod 775 teleyapper.php
    chown asterisk:asterisk teleyapper.php

    For Asterisk@Home 2.2 users:


    cd /var/lib/asterisk/agi-bin
    wget http://nerdvittles.com/aah2/teleyapper.zip
    unzip teleyapper.zip
    rm teleyapper.zip
    chmod 775 teleyapper.php
    chown asterisk:asterisk teleyapper.php

    The teleyapper.php script has a number of configuration options including a debug log. Edit the file carefully while positioned in the correct directory: nano -w teleyapper.php. And save your changes when you're finished: Ctrl-X, Y, then press Enter. All of the options are shown below.

    $numcallattempts=3 ;
    $calldelaybetweenruns=20 ;
    $callspread=1 ;
    $debug = 1;
    $newlogeachdebug = 1;
    $emaildebuglog = 0;
    $email = "yourname@yourdomain" ;
    $trunk = "sip/telasip-gw" ;
    $callerid = "6781234567" ;

    The first two options are not yet implemented so leave them alone. The callspread variable determines the spacing of calls to your various callees. The default is one minute which means the call to the second callee begins one minute after the first one starts. If your broadcast message is more than about 20 seconds long, you probably will need to increase this number to 2 to allow sufficient time to complete the first call before the next one begins. Otherwise, calls will fail if you only have a single outbound trunk.

    The debug flags in this file are set the same way as in the checkgroup.php script above: 1 means yes, and 0 means no. The default settings are to create a new log file (/var/log/asterisk/teleyapper.txt) each time the script is executed. This doesn't take up much room and is always there for you to read if something comes unglued: cat /var/log/asterisk/teleyapper.txt. There are some other options. You can turn off the log file entirely ($debug=0). You can choose not to erase the previous log file each time the script is run ($newlogeachdebug=0) in which case the file continues to grow until your hard disk fills up. And you can have the log file emailed to you each time the script is executed ($emaildebuglog=1) by also adding your email address ($email=youremailaddress).

    [UPDATED: 1/13/05] Two settings you will need to adjust to get ANY calls to complete properly are the trunk and callerid variables. If you wish to use a specific trunk in your dialplan for outbound calls, the syntax for the outbound trunk is the same as it is in your dialplan, e.g. sip/telasip-gw or iax2/voxee. Look at the OUT settings at the top of your extensions_additional.conf file if you're not sure. At the request of a number of users, we've now added a new option which allows all outbound TeleYapper calls to be placed using the default dialplan rules on your server. The advantage of this approach is that different VoIP providers can be used automatically for different types of calls in your TeleYapper database. To use your default dial rules, set the trunk to local and TeleYapper will handle the rest of the setup for you. For those that installed TeleYapper before January 13, simply repeat the download and install steps for teleyapper.php above. Our thanks to both Tracy Carlton at VOIPSpeak and chandave at the Voxilla forum for the default dialplan solution.

    The callerid variable should be set to the callerid number of your outbound trunk unless your service provider allows callerid spoofing (most don't!). The callerid setting is ignored if you choose to use your default dialplan rules with a trunk setting of local. Don't delete the variable! Just leave the default value.

    Finally keep in mind that the format of the numbers to be dialed in your database must exactly match the syntax your trunk provider is expecting to see unless you're using your default dialplan rules. Otherwise, all of the outbound calls will fail. For example, if your provider requires that calls begin with a 1 followed by a 3-digit area code and 7-digit number, then that's the way the numbers must be entered in your TeleYapper database.


    Installing teleyapper2.php AGI Script. This code is also new. Install the teleyapper2.php script in your /var/lib/asterisk/agi-bin directory, change file ownership and permissions on the file, and decide if you want to adjust the default debug configuration setup. Log in to your Asterisk server as root, and then execute the following commands:

    For Asterisk@Home 1.5 users:


    cd /var/lib/asterisk/agi-bin
    wget http://nerdvittles.com/aah15/teleyapper2.zip
    unzip teleyapper2.zip
    rm teleyapper2.zip
    chmod 775 teleyapper2.php
    chown asterisk:asterisk teleyapper2.php

    For Asterisk@Home 2.2 users:


    cd /var/lib/asterisk/agi-bin
    wget http://nerdvittles.com/aah2/teleyapper2.zip
    unzip teleyapper2.zip
    rm teleyapper2.zip
    chmod 775 teleyapper2.php
    chown asterisk:asterisk teleyapper2.php

    The only configuration options in the teleyapper2.php script are for the debug log on individual calls that are placed. We recommend you leave the existing settings, or you'll get a new email every time a call is placed by TeleYapper. You can edit the file while positioned in the correct directory: nano -w teleyapper2.php. And save your changes when you're finished: Ctrl-X, Y, then press Enter. All of the options are shown below.

    $debug = 1;
    $emaildebuglog = 0;
    $email = "yourname@yourdomain" ;

    The debug flags in this file are set the same way as in the teleyapper.php script above: 1 means yes, and 0 means no. The default settings are to append individual call information onto the teleyapper.txt log file (/var/log/asterisk/teleyapper.txt) each time a new call is placed. Unless you're planning to call hundreds of thousands of people, this doesn't take up much room and is there for you when something comes unglued. The other options are as follows. You can turn off the individual call logging entirely ($debug=0). And you can have the entire teleyapper.txt log file emailed to you each time a call is placed ($emaildebuglog=1) by also adding your email address ($email=youremailaddress). For your initial test calls, this may be desirable just so you can see what's going on ... if you're too lazy to read the log. In the words of Forest Gump, "That's all I wanna say about that."


    Taking TeleYapper for a Spin. Once you restart Asterisk (amportal stop then amportal start), you should have a Broadcast Message System that works. First, start up the Asterisk Command Line Interface (CLI) by typing asterisk -r from the command prompt on your system after you’ve logged in as root. Then issue the following command: set verbose 10. The CLI now will track the progress of your TeleYapper sessions.

    Using phpMyAdmin, add your cellphone number to your TeleYapper database and specify Group 0 for the entry. Now dial 674 and provide your password, record a message (Option #2), and then place a call (Option #3) to Group 0. Press 1 to kick off the TeleYapper calling spree. Check your CLI and TeleYapper logs if your cellphone doesn't ring in the next two minutes. Enjoy!

    TeleYapper Wish List. Some things are still on our TO-DO list, and we'll get to them one of these days. For those with loads of outbound trunks, we'll try to add a feature that lets you adjust the number of simultaneous oubound calls. For those that want to use the system to send out appointment reminders on dates in the future, we've completed that project. Here's the link. TeleYapper does not yet automatically redial failed calls except in the reminder system, but we plan to address that down the road, too. We also plan to build a web interface to the MySQL database which will let you add, edit, and delete entries as well as run some simple reports. If you have other suggestions, post a comment.

    Want More Projects? For a complete catalog of all our previous Asterisk projects, click here. For the most recent articles including those you missed over the Christmas and New Year's holidays, click here and just scroll down the page.

    From Our Legal Department, moi: The TeleYapper product name (our feeble attempt at humor through parody) has absolutely no affiliation with TeleZapper, the terrific hardware product designed to keep telemarketers from bugging the hell out of you while you're eating your dinner. We confess that our sense of humor got the better of us in coming up with the name for this non-commerical (aka "free") utility designed primarily as an educational vehicle to assist the Asterisk community in recognizing the almost limitless potential of AGI and PHP programming. Our parody seeks to amuse, not to confuse. Our telephony software Yaps. Their telephony hardware Zaps. Other than a telephone line, there is no product similarity as the two conjoined words make clear. And, yes, that is the whole point! The products are opposites, not identical nor even similar. One letter makes all the difference in Night and Light. So it is with Yapper and Zapper. Brand confusion in trademark law arises from synonyms, not antonyms. It is systems like what we're writing about today that TeleZapper is designed to protect against. And it does that very well. In fact, we use TeleZapper hardware in our own home and have for many years. The only problem, of course, is when that tornado comes rolling down the neighbor's street, it would have been nice to get that automated phone call from TeleYapper at the neighborhood headquarters. But, who cares, right? It's only your house. Class dismissed.

    Nerd Vittles Allison Fund. One of our next projects will be an Appointment Reminder System. Using a touchtone phone, you can schedule an unlimited number of customized voice reminders for delivery at any future time to any phone numbers you desire. To make it work, we need customized voice prompts for the IVR interface. Some of our future projects will need these as well. Unfortunately, these cost money. Allison, who records the Asterisk voice prompts for Digium, needs to eat. And so do we. We're springing for the costs of these voice prompts up front, but this is your golden opportunity to contribute to the cause. Provided we raise enough money to cover the costs, we'll put the voice prompts in the public domain. If not, when the project is presented, those that didn't contribute will have to record their own voice prompts in order to use the system. The rest will get the Allison prompts either on a password-protected web site or by email. Seems fair, right? To make a $5 or $10 contribution via PayPal using any major credit card, just click on this link. If the pot grows to over a million bucks, we promise never to ask for money again. Thanks in advance for your support.

    Who Is This Guy? Ward Mundy, the author of this Asterisk@Home series of articles, is a retired attorney who spent more than 30 years providing legal and technology assistance to the federal courts in the United States. Today he serves as a principal in Ward Mundy & Associates, a technology consulting firm in Atlanta, Georgia.

    The Best of Santa: Introducing the Nokia 770 Internet Tablet.everything

    Ever wished someone would come up with a portable Wi-Fi and Bluetooth-enabled device with a web browser, email client, news reader, PDF viewer, streaming audio and video players, and a SIP phone all rolled into a Linux-based, Internet Tablet? Well, it’s here with the exception of the SIP phone which is scheduled for early 2006. It’s about the size of your hand. And, if games are your thing, there’s Chess, Mahjong, Marbles, and virtually any other game that has been ported to the ARM version of Slackware including clones of Tetris, IceBreaker, Minesweeper, Doom, and on and on.

    The good folks at Nokia (yep, the cellphone people) finally have shipped the Nokia 770. And what a toy it is: the perfect addition to every commuter train ride or Starbucks visit. You may never travel with a notebook PC again. By loading an X terminal emulator, you have a Linux command prompt that will let you load and install virtually any Debian application on the planet: ssh, rsync, Office-compatible apps, VNC, NFS, GAIM IM, SQLite, and yes, someone has even ported the Asterisk® PBX to the 770. For those with a compatible Bluetooth cellphone, you can use your phone as your 770’s Internet link when there’s no Wi-Fi access point in your vicinity. It’s a bit quirky, but it does work with both CDMA and GSM Treo 650’s. And the best news of all: the Nokia 770 sports a high-res color screen that will have you believing you’re still sitting at your desktop PC. Best price: $349 at CompUSA this week.

    If you want to learn more, start at Nokia’s site for Flash-based demonstrations (which play equally well on the Nokia 770 incidentally). Then visit the Nokia-supported development site, maemo.org, and have a look at the long list of apps which already are available and are free to download. And then head over to Vidar Madsen’s and Russell Beattie’s sites to see what’s new in the Nokia 770 universe. And by all means pay a visit to Ari Jaaksi, whose Open Source team at Nokia made it all possible. For a more detailed review, visit this ArsTechnica link. Still have questions? Check out the Internet Tablet Talk Forums.

    Introducing TeleYapper: Free Asterisk Message Broadcasting System

    There's nothing quite like starting the new year with an application that exposes an ugly bug in Asterisk® 1.2, but that's what yesterday's original article did. So here's a second stab at the article ... and Asterisk ... without reliance on the buggy code. The revised article requires and will work reliably with Asterisk@Home 1.5 or Asterisk@Home 2.2. For those using Asterisk@Home 2.4 or later, use this updated tutorial and software.

    It's free software day again at Nerd Vittles, and today we're pleased to (re)introduce the first of several tutorials on TeleYapper, an Asterisk@Home-based telephone broadcasting service for neighborhoods, schools, little leagues, fundraisers, municipal governments, and anyone else that just wants to pester folks with annoying, but free, prerecorded phone calls. It works like this. You create a recorded message using Asterisk. Then you create a list of phone numbers to call in a MySQL database using a tool such as phpMyAdmin which is bundled with Asterisk@Home. Finally, you place a phone call to kick off TeleYapper. The software will dutifully swing into action and call every phone number from any of ten calling categories that you specify when you set up your database of callees. TeleYapper then will deliver the message you've recorded. It's even smart enough to detect answering machines and wait for the announcement to end before playing your message. In short, it works much like call-em-all.com and numerous other telephone broadcasting services with one important difference: TeleYapper is FREE! So, instead of paying 15¢ a call or $35 to $100 a month for a commercial service or spending thousands of dollars for a commercial dialer, now you can do it yourself using TeleYapper and your (also free) Asterisk@Home 1.5 or 2.2 PBX. Of course, it also gives us an opportunity to introduce you to some new Asterisk@Home tips and tricks, not to mention bugs, that should keep you burning the midnight oil (like we did last night) with enhancements and tweaks for the next few weeks.

    What's the catch? Well, there are a couple actually. If you have residential VoIP service with unlimited calling, be very careful about using this software without first checking with your provider or at least reading your provider's terms of service. Many prohibit such uses of residential VoIP service, and it's very easy for automated systems to figure out what you're doing since TeleYapper can place literally hundreds of calls per hour in very rapid succession. We plan to throttle the application down to 60 calls per hour, but it still will set off red flags with many VoIP providers. If, on the other hand, you're paying by the call or by the minute, those VoIP providers typically could care less and some will even let you specify your CallerID for such calls. There's nothing quite like using the White House phone number to relay those Little League sports scores to your friends and neighbors. Heh heh!

    In case you've never gotten to experience pre-recorded phone calls before (not likely in the U.S. of A!), think of it as podcasting in reverse. It's the old "don't call us, we'll call you" deal. Instead of people pulling content from your site, you push it to theirs ... in this case, their phones. But seriously, there are some legitimate uses for such a tool other than bugging the crap out of your neighbors. Here are a few that come to mind: event reminders, announcement of sports scores or practice times and locations, emergency notifications, medical appointment reminders, inclement weather alerts and schedules (think T-O-R-N-A-D-O!), neighborhood security warnings, schedule changes, and utility outage alerts. In short, such a system is ideal for many constructive purposes in the business world as well as in charitible organizations, schools, athletic programs, and neighborhood associations. But, for most of these purposes, you need the callee's written permission before making any calls. Just don't abuse it, and the feds and your neighbors won't come calling on you. We strongly recommend that you either obtain the written permission of every person you intend to call using this software or consult with an attorney before making any calls because of new Do Not Call legislation enacted in many states as well as the federal Do Not Call List. Our foreign visitors are well advised to take similar precautions. Failure to heed this advice may result in serious civil and criminal penalties including substantial fines imposed on a per call basis. For an excellent summary of current statutes in the United States, visit this site which pretty well documents why you still need either a TeleZapper hardware device or Asterisk's Zapateller software to protect your own phones from unwanted calls.


    Creative Commons LicenseLicensing. We are retaining ownership of this software as well as the copyright. It is licensed for use under the terms of the Creative Commons Attribution Non-Commercial license. A Plain English summary is available here. We've done this primarily to do our part to stamp out the telemarketing creeps of the world. Those wishing to use TeleYapper in a commercial environment must first request a license outlining your proposed terms of use. We will promptly respond with a yay or nay. Telemarketers need not apply!

    Editor's Note: We'd suggest you stop reading here and move on to Part II which covers everything you'll need to get TeleYapper yapping. You'll also avoid having to do some things twice.

    In this updated first installment, we'll put the framework in place to let you actually record messages and begin building your MySQL database of callees to support TeleYapper calls. We'll also point out some of the differences between AAH 1.5 and AAH 2.2 for those of you contemplating a career in Asterisk programming. Part I also will give you something to play with until we tie everything together and get TeleYapper dialing away in Part II. And someday, if we get a burst of energy, we may actually build a little database application like AsteriDex to help you add and edit entries in your TeleYapper database instead of having to rely upon phpMyAdmin. But, first things first.

    Adding TeleYapper to Your Dialplan. We need a simple facility for building voice messages for the TeleYapper project and for managing the call process. Using some of Asterisk's built-in tools and voice prompts, we've created something that's easy to use and simple to maintain. While the voice prompts are not quite perfect for this application, they're close enough for government work. Using a web browser, open the Asterisk Management Portal (AMP) by entering the IP address of your Asterisk machine. To add TeleYapper to your dialplan, just cut-and-paste the following code into the [from-internal-custom] context near the top of extensions_custom.conf: AMP->Maintenance->Config Edit->extensions_custom.conf. Be sure to change the 1234 password below to something secure for your system since this will be used to gain access to your TeleYapper system!

    exten => 674,1,Answer ; dial MSG on any extension to manage your TeleYapper system
    exten => 674,2,Wait(1)
    exten => 674,3,Authenticate(1234)
    exten => 674,4,Goto(yapper,s,1)

    If you're a long-time reader of Nerd Vittles and you're using either our Stealth AutoAttendant or some other AutoAttendant, then you already know why you need to be careful about putting extensions like 6-7-4 in your extensions_custom.conf file because anyone can call you, dial 6-7-4 while your AutoAttendant is playing, and insert their own obscene message into your TeleYapper system. The solution is adding your own secure password in line 3 above rather than using the default 1234. Another precaution you should always perform is to first play your outgoing TeleYapper message to yourself to make certain it says what you think it should before you kick off a dialing spree to a thousand of your closest friends or business associates.


    Adding the TeleYapper IVR. Now that we have a way to call into the TeleYapper system, we need something for it to do once we connect. So you'll need to cut-and-paste some code to the bottom of the extensions_custom.conf file while you still have the file open. Just scroll down to the bottom of the file. The actual code you need to paste depends upon the version of Asterisk@Home you're running. We initially thought we could demonstrate how to use Asterisk's built-in MySQL tools for some of the IVR interaction, but there appears to be a bug in Asterisk 1.2 upon which AAH 2.2 relies. Every query yields a zero record set when a search is implemented using our original code so that obviously won't be of much use with AAH 2.2. Another issue that those using AAH 1.5 should be aware of is this. Asterisk has no built in MySQL function to return a count of records matching a given search criteria. The only way to get the count is to loop through all the matching records and count them as you go. This works fine with a handful of records, but I have no idea how it would work with 500 or more records. Using a PHP/AGI script on the other hand lets us decipher the count of matching records for a search in one line of code. Bottom line: We're providing the "old" internal way to use MySQL because no one ever has correctly documented how it works, and it does work with Asterisk@Home 1.5. Then we'll provide the "new" PHP/AGI script version for use with Asterisk@Home 1.5. And finally we'll provide the "new" PHP/AGI script version for use with Asterisk@Home 2.2. Why two new versions? Well, the Asterisk developers changed the way variables are stored in Asterisk 1.09 (included in AAH 1.5) and Asterisk 1.2.1 (included in AAH 2.2). Dumb move! It's one thing to add new commands to a programming language (which Asterisk is). It's quite another to eliminate existing commands (which breaks functioning code) or to change the way variables are initialized or set (which yields different results when existing code is executed). End of rant.

    Option #1 for Asterisk@Home 1.5 Only. For those that want to experiment with using Asterisk's internal MySQL commands, here is the original TeleYapper code for you to cut-and-paste to the bottom of extensions_custom.conf. This code only works with Asterisk@Home 1.5, not Asterisk@Home 2.2:

    [yapper]
    exten => s,1,DigitTimeout(7)
    exten => s,2,ResponseTimeout(10)
    exten => s,3,Background(T-to-hear-cur-ancmnt)
    exten => s,4,Background(press-1)
    exten => s,5,Background(to-rerecord-yr-message)
    exten => s,6,Background(press-2)
    exten => s,7,Background(to-place-outgoing-call)
    exten => s,8,Background(press-3)
    exten => s,9,Background(to-hear-menu-again)
    exten => s,10,Background(press-4)
    exten => s,11,Background(to-hang-up)
    exten => s,12,Background(press-5)
    exten => 1,1,Playback(custom/broadcast)
    exten => 1,2,Wait(2)
    exten => 1,3,Goto(s,3)
    exten => 2,1,Goto(yapper2,s,1)
    exten => 3,1,Goto(yapper-options,s,1)
    exten => 4,1,Goto(s,3)
    exten => 5,1,Playback(goodbye)
    exten => 5,2,Goto(h,1)
    exten => t,1,Goto(s,3)
    exten => i,1,Goto(s,3)
    exten => o,1,Goto(s,3)
    exten => h,1,Hangup

    [yapper2]
    exten => s,1,Playback(after-the-tone)
    exten => s,2,Playback(say-temp-msg-prs-pound)
    exten => s,3,Wait(2)
    exten => s,4,Record(custom/broadcast:gsm)
    exten => s,5,Wait(2)
    exten => s,6,Playback(custom/broadcast)
    exten => s,7,Wait(2)
    exten => s,8,Playback(your-msg-has-been-saved)
    exten => s,9,Wait(2)
    exten => s,10,Goto(yapper,s,1)

    [yapper3]
    exten => s,1,SetGlobalVar(COUNTER=0)
    exten => s,2,MYSQL(Connect connid localhost root passw0rd teleyapper)
    exten => s,3,MYSQL(Query resultid ${connid} SELECT `*` FROM `callees` WHERE `group` = ${GROUP})
    exten => s,4,MYSQL(Fetch foundRow ${resultid} var1 var2 var3 var4 var5 var6) ; fetch row
    exten => s,5,GotoIf($["${foundRow}" = "1"]?6:9) ; leave loop if no row found
    exten => s,6,NoOp(${var2}: ${var3})
    exten => s,7,SetGlobalVar(COUNTER=$[${COUNTER} + 1])
    exten => s,8,Goto(4) ; continue loop if row found
    exten => s,9,MYSQL(Clear ${resultid})
    exten => s,10,MYSQL(Disconnect ${connid})
    exten => s,11,GotoIf($["${COUNTER}" = "0"]?9,1)
    exten => s,12,Playback(the-num-i-have-is)
    exten => s,13,SayNumber(${COUNTER})
    exten => s,14,Playback(vm-messages)
    exten => s,15,Wait(1)
    exten => s,16,DigitTimeout(7)
    exten => s,17,ResponseTimeout(10)
    exten => s,18,Background(to-call-this-number)
    exten => s,19,Background(press-1)
    exten => s,20,Background(to-hang-up)
    exten => s,21,Background(press-2)
    exten => 1,1,MYSQL(Disconnect ${connid})
    exten => 1,2,Goto(yapper-gen,s,1)
    exten => 2,1,Playback(goodbye)
    exten => 2,2,MYSQL(Disconnect ${connid})
    exten => 2,3,Hangup
    exten => 9,1,Playback(dir-nomatch)
    exten => 9,2,Wait(1)
    exten => 9,3,MYSQL(Disconnect ${connid})
    exten => 9,4,Goto(yapper,s,1)
    exten => t,1,Playback(goodbye)
    exten => t,2,MYSQL(Disconnect ${connid})
    exten => t,3,Hangup
    exten => i,1,Playback(goodbye)
    exten => i,2,MYSQL(Disconnect ${connid})
    exten => i,3,Hangup
    exten => s,103,MYSQL(Disconnect ${connid})
    exten => s,104,Hangup
    exten => h,1,MYSQL(Disconnect ${connid})

    [yapper-options]
    exten => s,1,Wait(1)
    exten => s,2,Playback(you-have-these-options)
    exten => s,3,Playback(digits/0)
    exten => s,4,Playback(through)
    exten => s,5,Playback(digits/9)
    exten => s,6,Playback(press-star-cancel)
    exten => s,7,SetGlobalVar(GROUP=0)
    exten => s,8,Read(GROUP,vm-enter-num-to-call,1)
    exten => s,9,NoOp(${GROUP})
    exten => s,10,GotoIf($["foo${GROUP}" = "foo"]?s,78)
    exten => s,11,GotoIf($["foo${GROUP}" = "foo*"]?s,88)
    exten => s,12,GotoIf($["foo${GROUP}" = "foo#"]?s,98)
    exten => s,13,Goto(yapper3,s,1)
    exten => s,78,Playback(connection-timed-out)
    exten => s,79,Wait(1)
    exten => s,80,Goto(yapper,s,1)
    exten => s,88,Playback(cancelled)
    exten => s,89,Wait(1)
    exten => s,90,Goto(yapper,s,1)
    exten => s,98,Playback(option-not-implemented)
    exten => s,99,Goto(yapper,s,1)

    [yapper-gen]
    exten => s,1,Playback(speed-dial)
    exten => s,2,Playback(activated)
    exten => s,3,Wait(1)
    exten => s,4,Playback(goodbye)
    exten => s,5,Hangup

    Option #2 for Asterisk@Home 1.5 Only. For those that want to use the new PHP/AGI scripting solution, here is the new TeleYapper code for you to cut-and-paste to the bottom of extensions_custom.conf. This code only works with Asterisk@Home 1.5, not Asterisk@Home 2.2:

    [yapper]
    exten => s,1,DigitTimeout(7)
    exten => s,2,ResponseTimeout(10)
    exten => s,3,Background(T-to-hear-cur-ancmnt)
    exten => s,4,Background(press-1)
    exten => s,5,Background(to-rerecord-yr-message)
    exten => s,6,Background(press-2)
    exten => s,7,Background(to-place-outgoing-call)
    exten => s,8,Background(press-3)
    exten => s,9,Background(to-hear-menu-again)
    exten => s,10,Background(press-4)
    exten => s,11,Background(to-hang-up)
    exten => s,12,Background(press-5)
    exten => 1,1,Playback(custom/broadcast)
    exten => 1,2,Wait(2)
    exten => 1,3,Goto(s,3)
    exten => 2,1,Goto(yapper2,s,1)
    exten => 3,1,Goto(yapper-options,s,1)
    exten => 4,1,Goto(s,3)
    exten => 5,1,Playback(goodbye)
    exten => 5,2,Goto(h,1)
    exten => t,1,Goto(s,3)
    exten => i,1,Goto(s,3)
    exten => o,1,Goto(s,3)
    exten => h,1,Hangup

    [yapper2]
    exten => s,1,Playback(after-the-tone)
    exten => s,2,Playback(say-temp-msg-prs-pound)
    exten => s,3,Wait(2)
    exten => s,4,Record(custom/broadcast:gsm)
    exten => s,5,Wait(2)
    exten => s,6,Playback(custom/broadcast)
    exten => s,7,Wait(2)
    exten => s,8,Playback(your-msg-has-been-saved)
    exten => s,9,Wait(2)
    exten => s,10,Goto(yapper,s,1)

    [yapper3]
    exten => s,1,AGI(checkgroup.php|${GROUP})
    exten => s,2,NoOp(tmp variable: ${tmp})
    exten => s,3,GotoIf($[${tmp} = 0]?9,1)
    exten => s,4,SetGlobalVar(COUNTER=${tmp})
    exten => s,5,Playback(the-num-i-have-is)
    exten => s,6,SayNumber(${COUNTER})
    exten => s,7,Playback(vm-messages)
    exten => s,8,Wait(1)
    exten => s,9,DigitTimeout(7)
    exten => s,10,ResponseTimeout(10)
    exten => s,11,Background(to-call-this-number)
    exten => s,12,Background(press-1)
    exten => s,13,Background(to-hang-up)
    exten => s,14,Background(press-2)
    exten => 1,1,Goto(yapper-gen,s,1)
    exten => 2,1,Playback(goodbye)
    exten => 2,2,Hangup
    exten => 9,1,Playback(dir-nomatch)
    exten => 9,2,Wait(1)
    exten => 9,3,Goto(yapper,s,1)
    exten => t,1,Playback(goodbye)
    exten => t,2,Hangup
    exten => i,1,Playback(goodbye)
    exten => i,2,Hangup
    exten => h,1,Hangup

    [yapper-options]
    exten => s,1,Wait(1)
    exten => s,2,Playback(you-have-these-options)
    exten => s,3,Playback(digits/0)
    exten => s,4,Playback(through)
    exten => s,5,Playback(digits/9)
    exten => s,6,Playback(press-star-cancel)
    exten => s,7,SetGlobalVar(GROUP=0)
    exten => s,8,Read(GROUP,vm-enter-num-to-call,1)
    exten => s,9,NoOp(${GROUP})
    exten => s,10,GotoIf($["foo${GROUP}" = "foo"]?s,78)
    exten => s,11,GotoIf($["foo${GROUP}" = "foo*"]?s,88)
    exten => s,12,GotoIf($["foo${GROUP}" = "foo#"]?s,98)
    exten => s,13,Goto(yapper3,s,1)
    exten => s,78,Playback(connection-timed-out)
    exten => s,79,Wait(1)
    exten => s,80,Goto(yapper,s,1)
    exten => s,88,Playback(cancelled)
    exten => s,89,Wait(1)
    exten => s,90,Goto(yapper,s,1)
    exten => s,98,Playback(option-not-implemented)
    exten => s,99,Goto(yapper,s,1)

    [yapper-gen]
    exten => s,1,Playback(speed-dial)
    exten => s,2,Playback(activated)
    exten => s,3,Wait(1)
    exten => s,4,Playback(goodbye)
    exten => s,5,Hangup

    Option #3 for Asterisk@Home 2.2 Only. As indicated, only the new PHP/AGI scripting solution works with Asterisk@Home 2.2. Here is the new TeleYapper code for you to cut-and-paste to the bottom of extensions_custom.conf. Because of the way global variables are set, this code only works with Asterisk@Home 2.2, not Asterisk@Home 1.5:

    [yapper]
    exten => s,1,DigitTimeout(7)
    exten => s,2,ResponseTimeout(10)
    exten => s,3,Background(T-to-hear-cur-ancmnt)
    exten => s,4,Background(press-1)
    exten => s,5,Background(to-rerecord-yr-message)
    exten => s,6,Background(press-2)
    exten => s,7,Background(to-place-outgoing-call)
    exten => s,8,Background(press-3)
    exten => s,9,Background(to-hear-menu-again)
    exten => s,10,Background(press-4)
    exten => s,11,Background(to-hang-up)
    exten => s,12,Background(press-5)
    exten => 1,1,Playback(custom/broadcast)
    exten => 1,2,Wait(2)
    exten => 1,3,Goto(s,3)
    exten => 2,1,Goto(yapper2,s,1)
    exten => 3,1,Goto(yapper-options,s,1)
    exten => 4,1,Goto(s,3)
    exten => 5,1,Playback(goodbye)
    exten => 5,2,Goto(h,1)
    exten => t,1,Goto(s,3)
    exten => i,1,Goto(s,3)
    exten => o,1,Goto(s,3)
    exten => h,1,Hangup

    [yapper2]
    exten => s,1,Playback(after-the-tone)
    exten => s,2,Playback(say-temp-msg-prs-pound)
    exten => s,3,Wait(2)
    exten => s,4,Record(custom/broadcast:gsm)
    exten => s,5,Wait(2)
    exten => s,6,Playback(custom/broadcast)
    exten => s,7,Wait(2)
    exten => s,8,Playback(your-msg-has-been-saved)
    exten => s,9,Wait(2)
    exten => s,10,Goto(yapper,s,1)

    [yapper3]
    exten => s,1,AGI(checkgroup.php|${GROUP})
    exten => s,2,NoOp(tmp variable: ${tmp})
    exten => s,3,GotoIf($[${tmp} = 0]?9,1)
    exten => s,4,Set(COUNTER=${tmp}|g)
    exten => s,5,Playback(the-num-i-have-is)
    exten => s,6,SayNumber(${COUNTER})
    exten => s,7,Playback(vm-messages)
    exten => s,8,Wait(1)
    exten => s,9,DigitTimeout(7)
    exten => s,10,ResponseTimeout(10)
    exten => s,11,Background(to-call-this-number)
    exten => s,12,Background(press-1)
    exten => s,13,Background(to-hang-up)
    exten => s,14,Background(press-2)
    exten => 1,1,Goto(yapper-gen,s,1)
    exten => 2,1,Playback(goodbye)
    exten => 2,2,Hangup
    exten => 9,1,Playback(dir-nomatch)
    exten => 9,2,Wait(1)
    exten => 9,3,Goto(yapper,s,1)
    exten => t,1,Playback(goodbye)
    exten => t,2,Hangup
    exten => i,1,Playback(goodbye)
    exten => i,2,Hangup
    exten => h,1,Hangup

    [yapper-options]
    exten => s,1,Wait(1)
    exten => s,2,Playback(you-have-these-options)
    exten => s,3,Playback(digits/0)
    exten => s,4,Playback(through)
    exten => s,5,Playback(digits/9)
    exten => s,6,Playback(press-star-cancel)
    exten => s,7,Read(tmp,vm-enter-num-to-call,1)
    exten => s,8,Set(GROUP=${tmp}|g)
    exten => s,9,NoOp(${GROUP})
    exten => s,10,GotoIf($["foo${GROUP}" = "foo"]?s,78)
    exten => s,11,GotoIf($["foo${GROUP}" = "foo*"]?s,88)
    exten => s,12,GotoIf($["foo${GROUP}" = "foo#"]?s,98)
    exten => s,13,Goto(yapper3,s,1)
    exten => s,78,Playback(connection-timed-out)
    exten => s,79,Wait(1)
    exten => s,80,Goto(yapper,s,1)
    exten => s,88,Playback(cancelled)
    exten => s,89,Wait(1)
    exten => s,90,Goto(yapper,s,1)
    exten => s,98,Playback(option-not-implemented)
    exten => s,99,Goto(yapper,s,1)

    [yapper-gen]
    exten => s,1,Playback(speed-dial)
    exten => s,2,Playback(activated)
    exten => s,3,Wait(1)
    exten => s,4,Playback(goodbye)
    exten => s,5,Hangup

    Saving Your Additions. When you've finished pasting one of the three chunks of code above, click the Update button at the bottom of the editing window. Now reload your Asterisk configs: AMP->Setup->Incoming Calls->Submit Changes and then click the Red Bar. Don't be dialing 674 just yet. We haven't installed your database to support the application.

    Installing the CheckGroup PHP/AGI Script. If you're running Asterisk@Home 1.5 with the Option #1 code above, you can skip this step. For those using one of the new PHP/AGI script solutions (Option #2 or Option #3 above), you'll need to install the checkgroup.php script in your /var/lib/asterisk/agi-bin directory and change file ownership and permissions on the file. Log in to your Asterisk server as root, and then execute the following commands:

    cd /var/lib/asterisk/agi-bin
    wget http://nerdvittles.com/checkgroup.zip
    unzip checkgroup.zip
    rm checkgroup.zip
    chmod 775 checkgroup.php
    chown asterisk:asterisk checkgroup.php

    This script includes a debug log. The default settings are to create a new log file (/var/log/asterisk/telecheck.txt) each time the script is executed. This doesn't take up much room and is always there for you to read if something comes unglued: cat /var/log/asterisk/telecheck.txt. There are some other options. You can turn off the log file entirely ($debug=0). You can choose not to erase the previous log file each time the script is run ($newlogeachdebug=0) in which case the file continues to grow until your hard disk fills up. And you can have the log file emailed to you each time the script is executed ($emaildebuglog=1) by adding your email address ($email=youremailaddress). The last option obviously assumes you have followed our previous tutorials and gotten outbound email working reliably on your system. The functions are controlled by the following lines at the top of the checkgroup.php file. 1 means yes, and 0 means no. Just edit the file carefully: nano -w checkgroup.php. And save your changes when you're finished: Ctrl-X, Y, then press Enter.

    $debug = 1;
    $newlogeachdebug = 1;
    $emaildebuglog = 0;
    $email = "yourname@yourdomain" ;


    TeleYapper in a Nutshell. Before we get to the database, let's briefly cover how the above code works. When you dial 674, the TeleYapper system will answer and prompt you for your password. Once you correctly enter the password, an interactive voice response (IVR) system will swing into action and give you several choices. That's what the [yapper] context handles. Pressing 1 lets you listen to your prerecorded TeleYapper message (if you have one). You don't yet so don't press 1. Pressing 2 lets you record a new TeleYapper message. This is handled by the [yapper2] context. Do this first and record something ... anything. You can rerecord a new message at any time by choosing option 2 again. Pressing 3 lets you kick off a TeleYapper dialing spree. It's handled by the [yapper3] context. Don't do this until we add your new database below, or you'll get smoke out of your system. For the technically inclined, [yapper3] in the Option #1 version actually performs some pretty hairy MySQL database tricks that won't work unless you're using Asterisk@Home 1.5. Pure Asterisk needs major patches to jump through the same hoops! (ATTN: Code Junkies -- Assuming you're running AAH 1.5, now you know how to query any MySQL database from inside your Asterisk dialplan!) Pressing 4 repeats the IVR menu choices. And pressing 5 disconnects you from the system.

    If you choose option 3 to initiate a TeleYapper calling session, the system will first prompt you for a group option number to use. This is managed by the [yapper-options] context. Simply stated, when you build your database of callees for TeleYapper, you can specify a one-digit group number for each entry in the file. Then, when you begin a calling session, you can narrow down the calling group by telling TeleYapper which group of callees to call. If you want a callee to be in more than one group, you simply enter that callee into the database multiple times with different group numbers. If you want everyone in the same group, then enter 0 for every person in your database.

    Once you specify the group number during your TeleYapper session, the system will actually look up and report back how many messages will be delivered to the callee group you've chosen. Allison will say something like this assuming there were 146 calls to be placed: "The number I have is one hundred and forty six messages." This will give you the count of qualifying records in the database and the option of proceeding with the calls or cancelling the transaction. In short, it's a safety mechanism to make sure you don't actually set off a calling spree to Group 7 with 500 callees when you meant to choose Group 8 with only 5. You'll still need to know what types of callees are in each group of callees that you create. TeleYapper doesn't quite do everything. Once we add our new database to your system and plug in a couple of entries, you'll have all week to experiment with the IVR system before TeleYapper actually begins making calls. So experiment away until you're comfortable with the menu system. As noted previously, we've endeavored to build this entire application using the voice prompts that are delivered with Asterisk@Home 1.5 or 2.x so they're not quite perfect. But they work reasonably well once you understand how the pieces fit together. You're supposed to be nodding in agreement now. If not, reread the last three paragraphs after you get another cup of coffee.


    Creating the TeleYapper Database. Now we're ready to create the MySQL database to support TeleYapper. The easiest way to do this is using the Asterisk Management Portal (AMP) to gain access to phpMyAdmin: AMP->Maintenance->phpMyAdmin. When phpMyAdmin loads, click on the SQL icon in the left column. When the SQL window appears, clear the existing SQL query and then cut-and-paste the following SQL code into that box and then click the Go button. When the import completes, click the teleyapper.callees table entry in the left column to open the file. Then click the Insert tab at the top of the right column to add entries to the table. You only need to add information for the name, phonenum, and group fields in the corresponding values column. The id, lastokcall, and lastcall fields should be left as is. The id field gets calculated automatically. The lastokcall will record the time and date of the last successful call using TeleYapper. And the lastcall field identifies what happened during the last call to this person, e.g. ok means the call was completed successfully, no answer means no one answered the call, or answering machine means an answering machine took the call.

    You can add up to two records at a time and, by clicking the Insert Another New Row button, you will be returned to this data entry screen after you save your entries by clicking the Go button. The name field allows you to quickly review entries you've made. It won't be used when making TeleYapper calls. The phonenum field is the important one. This is the exact dial string required to place a call on your Asterisk system to this callee using whatever VoIP or PSTN outbound trunk you plan to use with TeleYapper. For example, if your preferred provider requires 11-digit phone numbers with a 1, area code, and number, then that's the way the numbers should be entered into the TeleYapper database. The group field has already been discussed. Just enter a number between 0 and 9 to identify the group with whom this individual should be associated. Finally, after adding records to the table, you can click the Browse tab to review your entries. And, while Browsing, you can click the Pencil icon beside any record entry to edit it. Clicking the red X icon beside a record entry deletes the record. If, for some reason, you wish to delete ALL the records in the file, click the Empty tab at the top of the right column. Under no circumstances should you click on the Drop tab as this removes not only the table's contents but also the table structure itself. In short, you'd have to import the database table again.


    Taking TeleYapper for a Spin. We're ready to try out the TeleYapper IVR. First, start up the Asterisk Command Line Interface (CLI) by typing asterisk -r from the command prompt on your system after you've logged in as root. Then issue the following command: set verbose 10. The CLI now will tell you every phone number that would have been called during your TeleYapper session if you review the CLI history after performing the following steps.

    Dial 6-7-4 from an extension on your system and enter your password. Choose option 2 from the IVR menu and record an outgoing message to be delivered to your callees. Once you've recorded a message, you can play it back by choosing IVR option 1. Assuming you've added your callees in group 0, choose IVR option 3 and enter 0 for the calling group. The system should report how many messages will be delivered to this group. Select 1 to simulate placing calls to this group. Then check the CLI and make sure the phone numbers for each member of the group correctly appear in the CLI history. If everything went according to plan, you're ready to move on to TeleYapper, Part II.

    Coming Attractions: TeleYapper, Part II. In our next chapter (available here), we'll actually wire the pieces together so that you can make a phone call from anywhere and start up a TeleYapper calling session. We'll be using some more new Asterisk tricks to make all of this work. We obviously don't want to stay on the line while TeleYapper makes all the outbound phone calls so we'll be using Asterisk's hangup functionality to execute a PHP script which does the heavy lifting. We'll start by passing the group number variable and the count of callees to the PHP script. For all you code warriors, we'll document the steps necessary to get a PHP session initialized to talk with Asterisk. And we'll show you how to read in the variables passed from the Asterisk IVR session. Then we'll do some standard MySQL processing to look up the correct records to obtain the phone numbers to call. Then we'll build the scripts to actually place the phone calls and interact with someone or some answering machine at the other end of the line. And finally we'll introduce a clever trick to let you schedule and place all of the outbound calls in the background ... after your PHP session and initiating phone call have ended. What is it? Well, it's none other than Andy Wysocki's WakeUp Calls engine which is already built into Asterisk@Home. By sending a five-line script to the wakeup call directory, we can kick off a phone call to anyone at any time we specify. And then leave it to Asterisk to sequentially place the calls after we've headed out to lunch. Pretty neat!

    Want More Projects? For a complete catalog of all our previous Asterisk projects, click here. For the most recent articles including those you missed over the Christmas and New Year's holidays, click here and just scroll down the page.

    From Our Legal Department, moi: The TeleYapper product name (our feeble attempt at humor through parody) has absolutely no affiliation with TeleZapper, the terrific hardware product designed to keep telemarketers from bugging the hell out of you while you're eating your dinner. We confess that our sense of humor got the better of us in coming up with the name for this non-commerical (aka "free") utility designed primarily as an educational vehicle to assist the Asterisk community in recognizing the almost limitless potential of AGI and PHP programming. Our parody seeks to amuse, not to confuse. Our telephony software Yaps. Their telephony hardware Zaps. Other than a telephone line, there is no product similarity as the two conjoined words make clear. And, yes, that is the whole point! The products are opposites, not identical nor even similar. One letter makes all the difference in Night and Light. So it is with Yapper and Zapper. Brand confusion in trademark law arises from synonyms, not antonyms. It is systems like what we're writing about today that TeleZapper is designed to protect against. And it does that very well. In fact, we use TeleZapper hardware in our own home and have for many years. The only problem, of course, is when that tornado comes rolling down the neighbor's street, it would have been nice to get that automated phone call from TeleYapper at the neighborhood headquarters. But, who cares, right? It's only your house. Class dismissed.

    Who Is This Guy? Ward Mundy, the author of this Asterisk@Home series of articles, is a retired attorney who spent more than 30 years providing legal and technology assistance to the federal courts in the United States. Today he serves as a principal in Ward Mundy & Associates, a technology consulting firm in Atlanta, Georgia.

    The Music Frontier: Taming Streaming Audio for Music on Hold with Asterisk

    Using streaming audio with the Asterisk® music on hold facility is another one of those little gotcha's that gives a lot of folks problems so we decided to wrap up 2005 by outlining a working setup of streaming audio for both Asterisk@Home 1.5 and Asterisk@Home 2.2. And, yes, it'll work with either recent version of pure Asterisk with a little extra elbow grease.

    UPDATE: For releases of Asterisk after 1.4, this tutorial will not work. See our updated article for Asterisk 11.

    Prerequisites: Before streaming audio can be used for Music on Hold (MOH) with Asterisk, there are three essential pieces. First, you must have a source of streaming audio that works. Second, you need a streaming audio player on your Asterisk/Linux server that can "talk" to your Asterisk system. And, finally, Asterisk has to be properly configured to support streaming audio as the source for your music on hold.

    Choosing a Streaming Audio Source. An almost infinite variety of streaming audio exists on the net. If you're just getting into streaming audio, head over to Shoutcast.com for over 12,000 FREE sources to get you started. If you'd prefer to set up your own Shoutcast server, Nerd Vittles has previously covered solutions for both the Windows (WinAMP) and Mac (NiceCast) platforms. Unless computer viruses and Trojans (not that kind!) are your thing, buy a $500 Mac mini and call it a day. NiceCast works flawlessly. Insofar as Asterisk is concerned, here's the bottom line. If the streaming audio source you've chosen sounds like crap when you play it on your PC or Mac, it will sound the same way (or worse) as your MOH source. So start this project by picking a source that sounds good and be sure it plays reliably on your desktop PC or Mac before proceeding further. Keep in mind that anything above a 24K mono stream is wasted on a telephone call so there's no need to choose a 128K stereo audio stream unless you just want to eat up your bandwidth. And, finally, keep in mind that, unless you're using your own stream on your private LAN, the streaming audio will be using the same bandwidth that you need to support incoming and outgoing phone calls over your broadband connection. So less is more!

    Configuring an Asterisk MP3 Player. For those using Asterisk@Home 1.5 or a more recent version of Asterisk@Home 2.x, then you have a version of mpg123 that is suitable for playing streaming audio as your MOH source. If you're not sure, log in to your server and type mpg123 -v to see what version of mpg123 is installed on your system. You'll need at least 0.59r to "talk" to Asterisk properly. If you need an update, here's a tutorial that will get you up to speed.


    Building a Stream Directory. Next we need a directory to actually hold the contents of the stream while it's playing on your Music on Hold system. Log into your Asterisk server as root. Then switch users to asterisk: su asterisk. Now move to the default MOH directory: cd /var/lib/asterisk/mohmp3. Create a new directory to hold the streaming audio: mkdir stream. Switch to the new directory: cd stream. Now make an empty file to be used for the stream contents: touch stream.mp3. Close your asterisk user session by typing exit. That will leave you logged in as root.


    Configuring Asterisk for MOH Streaming Audio. Now we need to set up a music on hold channel for your streaming audio: nano -w /etc/asterisk/musiconhold.conf. If you're using your own streaming audio server, then the line you want to add at the bottom of the file will look something like this except with the actual internal IP address of your Shoutcast server and the correct port number of your audio stream. Hint: NiceCast tells you everything you need to know by clicking the Share button.

    stream => quietmp3:/var/lib/asterisk/mohmp3/stream,http://192.168.0.107:8000/

    If you're using an external source, then the line will look something like the following. Just right click on the streaming audio link you've found and save the address to your clipboard for pasting:

    stream => quietmp3:/var/lib/asterisk/mohmp3/stream,http://www.shoutcast.com/sbin/shoutcast-playlist.pls?rn=3281&file=filename.pls

    Once you've specified your audio stream, save the updated musiconhold config file: Ctrl-X,Y,then press enter.


    Testing Your MOH Stream with Asterisk. With everything now properly configured, let's set up an extension just to be sure it's working correctly. Edit your extensions_custom.conf file:

    nano -w /etc/asterisk/extensions_custom.conf

    Then add the following somewhere within the [from-internal-custom] context:

    exten => 466,1,Answer
    exten => 466,2,Playback(pls-hold-while-try)
    exten => 466,3,SetMusicOnHold,stream
    exten => 466,4,WaitMusicOnHold,300
    exten => 466,5,Hangup

    Once you've added this extension code, save the updated file: Ctrl-X,Y,then press enter. Now restart Asterisk: amportal stop then amportal start. Pick up one of the phones on your Asterisk system and dial 466. After you're connected, it may take up to 15 seconds for the streaming audio to begin, but this delay only occurs on the first connection. Once you've heard your audio stream playing, hang up and call back just to make sure.


    Configuring Streaming Audio as Default Music on Hold. Now that we have everything working, you may decide you'd prefer to replace your default MOH tunes with your new streaming audio source. It's easy. Edit the musiconhold config file again. Comment out the line beginning with "default" by inserting a semicolon at the beginning of the line. Then change the line we added which begins with the word "stream" so that it looks like this:

    default => quietmp3:/var/lib/asterisk/mohmp3/stream,http://192.168.0.107:8000/

    Save the updated file, and then restart Asterisk: amportal stop then amportal start.


    1-800-411-METROFree Directory Assistance Service Launched. For those not using BroadVoice (which now has the same 411 service), write this number down or add it to your Asterisk dialplan for free directory assistance calls in the United States: 1-800-411-6387. It's also a free VoIP call with the providers listed below once you set up an account with one of them. You can read our reviews of these providers here. Assuming you have an account, just add ONE of the following sets (that match the provider with whom you have set up an account) to the [from-internal-custom] context in extensions_custom.conf:


    exten => 411,1,Dial(IAX2/goiax/18004116387)   ; GoIAX Free Call
    exten => _1NXX5551212,1,Dial(IAX2/goiax/18004116387)
    exten => _NXX5551212,1,Dial(IAX2/goiax/18004116387)

    exten => 411,1,Dial(IAX2/fwd/*18004116387)    ; FWD Free Call
    exten => _1NXX5551212,1,Dial(IAX2/fwd/*18004116387)
    exten => _NXX5551212,1,Dial(IAX2/fwd/*18004116387)

    exten => 411,1,Dial(IAX2/teliax/18004116387)  ; Teliax Free Call
    exten => _1NXX5551212,1,Dial(IAX2/teliax/18004116387)
    exten => _NXX5551212,1,Dial(IAX2/teliax/18004116387)


    Farewell to WordPress Cut-And-Paste Nightmare. For long-time readers of this column, you know what a royal pain cutting-and-pasting has been at Nerd Vittles thanks to the WordPress blog's proclivity for changing quotation marks (used in many Asterisk commands) to 'smart quotes' and replacing double-hyphens (used in many Linux commands) to 'long hyphens.' The end result has been that, while the code worked great on our development systems, it blew up when you used cut-and-paste to move it to your Asterisk server. This all came to a head this past week when our article on faxing blew up on every reader's system because of the double-hyphens ... which we actually didn't know was a problem until several days after the article hit the street. Merry Christmas to us!

    Just like everything else that's great about the Open Source community, there is always someone smart enough not only to recognize a problem but also to fix it. So our hat is forever tipped to Alex King, one of WordPress's most ardent supporters. He wrote about the problem and then he single-handedly fixed it with his WP Unformatted Plugin. It just took us a while to discover it. There's nothing like a double helping of egg on your face to make you scratch a little harder for a solution. Now that we have the plugin, we'll be using it regularly and, as quickly as we can, we'll go back and rework all of our previous articles as well. So, hopefully the problem will go away for you and for us permanently. Should you see any code that still looks like it has quotation marks pointing in two different directions, please let us know. And ... thanks for your patience.

    Free Calls from Nerd Vittles. Celebrate the New Year with a free call on us and our friends at TelaSIP. You can read all about it here.

    Want More Projects? For a complete catalog of all of our Asterisk projects during 2005, click here. Have a Happy New Year, and we look forward to serving up loads of new Tips and Tricks for Asterisk in 2006!

    The Last Frontier: Handling Incoming Faxes with Asterisk

    We've covered lots of Asterisk® territory these past few months, but one of the real stumbling blocks has been reliable incoming fax support. Yes, Asterisk has some fax support built into the product; however, to use it you have to invest in Digium® hardware. Not that we're cheap or anything (we are actually!), but we wanted a fax solution that didn't rely on hardware. Stated another way, we wanted a pure software solution to faxing just like Asterisk provides a pure software solution to PBX telephony for those that want to use it. And we wanted reliable delivery of faxes using VoIP lines including IAX and SIP trunks, not just Ma Bell's PSTN lines. Well, we've finally found a solution, and it works reliably with Asterisk@Home 1.5 as well as Asterisk@Home 2.x. For those not using Asterisk@Home, the solution we're covering works reliably with Asterisk 1.09 as well as Asterisk 1.2 and 1.2.1. This tutorial assumes you're using Asterisk@Home, but the concepts will work equally well with "pure Asterisk" assuming you know what you're doing.

    2008 Update: This article has been updated to support Asterisk 1.4. Click here for the latest article.

    2011 Update: This article has been updated to support Asterisk 1.8 using HylaFax, AvantFax, and IAXmodem. Click here for the latest article.

    The way this works is pretty straightforward. A fax machine calls your regular Asterisk phone number, no dedicated fax line required. When Asterisk answers the call, it listens for a fax tone. If it hears one, it reroutes the incoming call to a context which then processes the incoming fax. The fax is saved as a TIFF image, converted to a PDF document, and emailed to the address specified in your Asterisk@Home setup: AMP->Setup->General Settings. If a fax tone isn't detected, the call is processed as an incoming voice call according to the rules you've set up for incoming calls.

    Prerequisites. As previously noted, you won't need any special hardware to make our incoming fax solution work with Asterisk. All you need is Asterisk@Home and Newman Telecom's NVfaxdetect, which is FREE for the downloading. You'll also need the Fax-to-PDF conversion tools which are included with Asterisk@Home. Your system also must be configured to send emails reliably. If you followed our tutorial for setting up your Asterisk@Home system, you should be all set. Otherwise, start there in the Activating Email Delivery of VoiceMail Messages section. Finally, if you're using an IAX or SIP trunk, you'll need a line from a VoIP provider with an uncompressed codec (such as ulaw), and you need an ITSP that knows what it's doing and supports incoming faxes. HINT: TelaSIP lines work. BroadVoice lines usually don't.


    An equally important requirement for this project is following along carefully with this tutorial. We're going to be recompiling a modified Asterisk as part of this project and, if you screw it up by not paying careful attention to the details, not only will your system not accept faxes, it probably won't accept phone calls either when we're finished. Having said that, there is nothing hard about this project. You just need to type carefully and not skip any steps. So let's get started.

    Installing Fax-to-PDF Conversion Tools. Let's begin with the easy stuff. First, you'll need to install the Fax-To-PDF conversion tools that are already scripted in Asterisk@Home. Log in to your server as root. At the command prompt, type install-pdf. Your system will whir away for a couple minutes after which you'll get a completed message that looks something like this:

    Running Transaction
    Installing: xorg-x11-font-utils ######################### [1/8]
    Installing: ttmkfdir ######################### [2/8]
    Installing: xorg-x11-xfs ######################### [3/8]
    Installing: chkfontpath ######################### [4/8]
    Installing: urw-fonts ######################### [5/8]
    Installing: VFlib2 ######################### [6/8]
    Installing: ghostscript-fonts ######################### [7/8]
    Installing: ghostscript ######################### [8/8]

    Installed: ghostscript.i386 0:7.07-33
    Dependency Installed: VFlib2.i386 0:2.25.6-25 chkfontpath.i386 0:1.10.0-2 ghostscript-fonts.noarch 0:5.50-13 ttmkfdir.i386 0:3.0.9-14.1.EL urw-fonts.noarch 0:2.2-6.1 xorg-x11-font-utils.i386 0:6.8.2-1.EL.13.20 xorg-x11-xfs.i386 0:6.8.2-1.EL.13.20
    Complete!

    For those running Asterisk with flavors of Linux other than CentOS, the install command you'll need above is yum -y install ghostscript.

    Routing Your Incoming Faxes. To specify where you want incoming faxes delivered, start up the Asterisk Management Panel (AMP) by pointing a web browser to the IP address of your Asterisk server. Then choose AMP->Setup->General Settings. In the Fax Machine section, leave the extension of fax machine set to System. For the email fax delivery address, enter a valid email address where you want the PDF copies of faxes to be sent. Click the Submit Changes button and then click the red bar to reload Asterisk.


    Downloading NVfaxdetect. Log in to your Asterisk server as root, stop Asterisk, and download NVfaxdetect using the following commands:

    amportal stop
    cd /usr/src/asterisk/apps
    wget http://www.newmantelecom.com/download/asterisk/faxdetect/1.0.6/app_nv_faxdetect.c

    NOTE: Some intermittent problems have been reported downloading the source code from Newman Telecom. If you have a problem, you can download the patched source code directly from Nerd Vittles. Here's the link for Asterisk@Home 1.5 systems:

    wget http://nerdvittles.com/aah15/app_nv_faxdetect.c

    And here's the link for Asterisk@Home 2.x systems:

    wget http://nerdvittles.com/aah2/app_nv_faxdetect.c

    Modifying the NVfaxdetect Source Code. Skip this section if you downloaded the source for your version of Asterisk@Home directly from Nerd Vittles. Otherwise, a change needs to be made in the NVfaxdetect source code whether you are running Asterisk@Home 1.5 or Asterisk@Home 2.x. Edit the source code by issuing the following command: nano -w app_nv_faxdetect.c.

    If you're using Asterisk@Home 1.5, search for the word CALLERID in the source file: Ctrl-W, CALLERID. When it finds the match, you'll be in the middle of three lines of code that look like this:

    // Use the second one for recent Asterisk releases
    #define CALLERID_FIELD cid.cid_num
    //#define CALLERID_FIELD callerid

    Change these three lines so that the first define line is commented out and the second one is uncommented. Your code should look like the following when you're finished:

    // Use the second one for recent Asterisk releases
    //#define CALLERID_FIELD cid.cid_num
    #define CALLERID_FIELD callerid

    Once you complete the change, save the file: Ctrl-X, Y, then press Enter.

    If you're using Asterisk@Home 2.x, we need to make a different change in the source code. Search for #include with the command: Ctrl-W, #include. When the match is found, move the cursor up one line, press the Enter key, and insert the following code:

  • Ignore the two dots in the right column above. WordPress quirk! Once you make this addition, save the file: Ctrl-X, Y, then press Enter.

    Adjusting Makefile. Regardless of your version of Asterisk@Home, we need to modify Makefile to include this code in the Asterisk executable: nano -w Makefile. With AAH 2.x, search for the word "experimental": Ctrl-W, experimental. Move down one line and press Enter. With AAH 1.5, search for the expression "#APPS+": Ctrl-W,#APPS+. Move up one line and press Enter. Now, with either version, add the following making sure there is no pound sign (#) at the beginning of the line:

    APPS+=app_nv_faxdetect.so

    Once you make this addition, save the file: Ctrl-X, Y, then press Enter.


    Recompiling Asterisk. Now we're ready to recompile and reinstall the Asterisk application with your new NVfaxdetect code. Issue the following commands:

    cd /usr/src/asterisk
    make
    make install

    You'll get some warning messages when the install completes. You can safely ignore them. When the install is finished, you can restart Asterisk and use the Command Line Interface (CLI) to make certain that NVfaxdetect installed properly:

    amportal start
    asterisk -r
    set verbose 10
    show application nvfaxdetect

    The following CLI message should be displayed:

    This application listens for fax tones (on IAX and SIP channels too)
    for waitdur seconds of time. In addition, it can be interrupted by digits,
    or non-silence. Audio is only monitored in the receive direction. If
    digits interrupt, they must be the start of a valid extension unless the
    option is included to ignore. If fax is detected, it will jump to the
    'fax' extension. If a period of non-silence greater than 'mindur' ms,
    yet less than 'maxdur' ms is followed by silence at least 'sildur' ms
    then the app is aborted and processing jumps to the 'talk' extension.
    If all undetected, control will continue at the next priority.
    waitdur: Maximum number of seconds to wait (default=4)
    options:
    'n': Attempt on-hook if unanswered (default=no)
    'x': DTMF digits terminate without extension (default=no)
    'd': Ignore DTMF digit detection (default=no)
    'f': Ignore fax detection (default=no)
    't': Ignore talk detection (default=no)
    sildur: Silence ms after mindur/maxdur before aborting (default=1000)
    mindur: Minimum non-silence ms needed (default=100)
    maxdur: Maximum non-silence ms allowed (default=0/forever)
    Returns -1 on hangup, and 0 on successful completion with no exit conditions.

    For questions or comments, please e-mail support@newmantelecom.com.

    Adjusting Your Dialplans to Implement Fax Detection. The final step in this process is to adjust the Asterisk dialplans to implement fax detection and process incoming faxes. Several different methods are shown below. Choose the one that applies to your deployment of Asterisk.


    For those using a standard Asterisk 1.5 or Asterisk 2.x install with no Stealth AutoAttendant, you'll need to make the following changes to both the [from-pstn-reghours] and [from-pstn-afthours] contexts in the extensions.conf config file.

    To modify [from-pstn-reghours], use the Asterisk Management Portal (AMP) to access AMP->Maintenance->Config Edit->extensions.conf->from-pstn-reghours. Delete all of the existing code:

    exten => s,1,GotoIf($[${FAX_RX} = disabled]?from-pstn-reghours-nofax,s,1:2) ; if fax detection is disabled, then jump to from-pstn-nofax - else continue
    exten => s,2,Answer
    exten => s,3,Wait(1)
    exten => s,4,SetVar(intype=${INCOMING})
    exten => s,5,Cut(intype=intype,-,1)
    exten => s,6,GotoIf($[${intype} = EXT]?7:9) ; If INCOMING starts with EXT, then assume its an extension
    exten => s,7,Wait(3) ;wait 3 more second to make sure this isn't a fax before dialing someone
    exten => s,8,Goto(ext-local,${INCOMING:4},1)
    exten => s,9,GotoIf($[${intype} = GRP]?10:12) ; If INCOMING starts with GRP, then assume its a ring group
    exten => s,10,Wait(3)
    exten => s,11,Goto(ext-group,${INCOMING:4},1)
    exten => s,12,GotoIf($[${intype} = QUE]?13:15)
    exten => s,13,Wait(3)
    exten => s,14,Goto(ext-queues,${INCOMING:4},1)
    exten => s,15,Goto(${INCOMING},s,1) ; not EXT or GR1 - it's an auto attendant
    exten => fax,1,Goto(ext-fax,in_fax,1)
    exten => h,1,Hangup

    Then insert the following new code in the [from-pstn-reghours] context and click the Update button:

    exten => s,1,GotoIf($[${FAX_RX} = disabled]?from-pstn-reghours-nofax,s,1:2); if fax detection is disabled, then jump to from-pstn-nofax - else continue
    exten => s,2,Answer
    exten => s,3,Playtones(ring) ; play fake ring so caller doesn't wonder whats going on
    exten => s,4,NVFaxDetect(10) ; while playing ring sound, detect faxes for 2 rings (goes to "fax" extension if detected)
    exten => s,5,SetVar(intype=${INCOMING})
    exten => s,6,Cut(intype=intype,-,1)
    exten => s,7,GotoIf($[${intype} = EXT]?8:9) ; If INCOMING starts with EXT, then assume its an extension
    exten => s,8,Goto(ext-local,${INCOMING:4},1)
    exten => s,9,GotoIf($[${intype} = GRP]?10:11) ; If INCOMING starts with GRP, then assume its a ring group
    exten => s,10,Goto(ext-group,${INCOMING:4},1)
    exten => s,11,GotoIf($[${intype} = QUE]?13:14)
    exten => s,13,Goto(ext-queues,${INCOMING:4},1)
    exten => s,14,Goto(${INCOMING},s,1) ; not EXT or GRP - it's an auto attendant
    exten => fax,1,Goto(ext-fax,in_fax,1)
    exten => h,1,Hangup

    Now we need to make a similar change in the [from-pstn-afthours] context. Replace the following code:

    exten => s,1,GotoIf($[${FAX_RX} = disabled]?from-pstn-afthours-nofax,s,1:2) ; if fax detection is disabled, then jump to from-pstn-nofax - else continue
    exten => s,2,Answer
    exten => s,3,Wait(1)
    exten => s,4,SetVar(intype=${AFTER_INCOMING})
    exten => s,5,Cut(intype=intype,-,1)
    exten => s,6,GotoIf($[${intype} = EXT]?7:9) ; If INCOMING starts with EXT, then assume its an extension
    exten => s,7,Wait(3) ;wait 3 more second to make sure this isn't a fax before dialing someone
    exten => s,8,Goto(ext-local,${AFTER_INCOMING:4},1)
    exten => s,9,GotoIf($[${intype} = GRP]?10:12) ; If INCOMING starts with GRP, then assume its a ring group
    exten => s,10,Wait(3)
    exten => s,11,Goto(ext-group,${AFTER_INCOMING:4},1)
    exten => s,12,GotoIf($[${intype} = QUE]?13:15)
    exten => s,13,Wait(3)
    exten => s,14,Goto(ext-queues,${AFTER_INCOMING:4},1)
    exten => s,15,Goto(${AFTER_INCOMING},s,1) ; not EXT or GR1 - it's an auto attendant
    exten => fax,1,Goto(ext-fax,in_fax,1)
    exten => h,1,Hangup

    Replace the above code with the following new code and then click the Update button:

    exten => s,1,GotoIf($[${FAX_RX} = disabled]?from-pstn-afthours-nofax,s,1:2); if fax detection is disabled, then jump to from-pstn-nofax - else continue
    exten => s,2,Answer
    exten => s,3,Playtones(ring) ; play fake ring so caller doesn't wonder whats going on
    exten => s,4,NVFaxDetect(10) ; while playing ring sound, detect faxes for 2 rings (goes to "fax" extension if detected)
    exten => s,5,SetVar(intype=${AFTER_INCOMING})
    exten => s,6,Cut(intype=intype,-,1)
    exten => s,7,GotoIf($[${intype} = EXT]?8:9) ; If INCOMING starts with EXT, then assume its an extension
    exten => s,8,Goto(ext-local,${AFTER_INCOMING:4},1)
    exten => s,9,GotoIf($[${intype} = GRP]?10:11) ; If INCOMING starts with GRP, then assume its a ring group
    exten => s,10,Goto(ext-group,${AFTER_INCOMING:4},1)
    exten => s,11,GotoIf($[${intype} = QUE]?13:14)
    exten => s,13,Goto(ext-queues,${AFTER_INCOMING:4},1)
    exten => s,14,Goto(${AFTER_INCOMING},s,1) ; not EXT or GRP - it's an auto attendant
    exten => fax,1,Goto(ext-fax,in_fax,1)
    exten => h,1,Hangup


    For those using the Nerd Vittles' Stealth AutoAttendant to process incoming calls, you'll need to adjust the extension 111 script in your extensions_custom.conf file. Here's the NVfaxdetect setup that we use which also includes the BlackList, CallerID, and Bluetooth Proximity Detection systems we've previously built. So, yes, we do eat our own dog food. Special note: We don't accept faxes on our Southern Bell PSTN line (111,6,Goto(9)). If you'd prefer to accept them, then change Goto(9) to Goto(7).

    exten => 111,1,LookupBlacklist ; If CID blacklisted, goto 102 ; Check for creeps
    exten => 111,2,Answer
    exten => 111,3,GotoIf($["${CALLERIDNUM:0:2}" = "00"]?4:7) ; Special Handling for SPA-3000
    exten => 111,4,SetCIDNum(${CALLERIDNUM:2})
    exten => 111,5,LookupBlacklist ; If CID blacklisted, goto 106 ; Gotta do it again after adjusting CallerID
    exten => 111,6,Goto(9)
    exten => 111,7,Playtones(ring) ; play fake ring so caller doesn't wonder whats going on
    exten => 111,8,NVFaxDetect(10) ; while playing ring sound, detect faxes for seconds (goes to "fax" extension if detected)
    exten => 111,9,SetMusicOnHold(default)
    exten => 111,10,Wait(1)
    exten => 111,11,GotoIf($["${CALLERIDNUM}" = ""]?who-r-u,s,1)
    exten => 111,12,GotoIf($["foo${CALLERIDNUM}" = "foo"]?who-r-u,s,1)
    exten => 111,13,GotoIf($["${CALLERIDNAME:0:9}" = "Anonymous"]?who-r-u,s,1)
    exten => 111,14,GotoIf($["${CALLERIDNAME:0:7}" = "Unknown"]?who-r-u,s,1)
    exten => 111,15,GotoIf($["${CALLERIDNUM:0:7}" = "Private"]?who-r-u,s,1)
    exten => 111,16,GotoIf($["${CALLERIDNAME:0:7}" = "Private"]?who-r-u,s,1)
    exten => 111,17,GotoIf($["${CALLERIDNUM:0:10}" = "Restricted"]?who-r-u,s,1)
    exten => 111,18,GotoIf($["${CALLERIDNAME:0:11}" = "OUT OF AREA"]?who-r-u,s,1)
    exten => 111,19,GotoIf($["${CALLERIDNUM:0:4}" = "PSTN"]?who-r-u,s,1)
    exten => 111,20,GotoIf($["${CALLERIDNUM:0:3}" = "199"]?who-r-u,s,1)
    exten => 111,21,GotoIfTime(${REGTIME}|${REGDAYS}|*|*?25)
    exten => 111,22,Background(silence/2)
    exten => 111,23,VoiceMail(204@default)
    exten => 111,24,Hangup
    exten => 111,25,AGI(calleridname.agi)
    exten => 111,26,Background(custom/welcome)
    exten => 111,27,DigitTimeout,2
    exten => 111,28,ResponseTimeout,2
    exten => 111,102,Goto(custom-blacklisted,s,1)
    exten => 111,106,Goto(custom-blacklisted,s,1)
    exten => fax,1,Goto(ext-fax,in_fax,1)
    exten => t,1,Background(pls-hold-while-try)
    exten => t,2,AGI(homecheck.agi)
    exten => t,3,GotoIf($["${WARD:0:2}" = "OU"]?custom-outhouse,s,1:4)
    exten => t,4,Dial(local/222@from-internal,20,m)
    exten => t,5,VoiceMail(204@default)
    exten => t,6,Hangup
    exten => t,105,Voicemail(204@default)
    exten => t,106,Hangup
    exten => i,1,Playback(wrong-try-again-smarty)
    exten => i,2,Goto(111,26)
    exten => o,1,Macro(rg-group,20,,200-201-204-210)
    exten => o,2,VoiceMail(204@default)
    exten => o,3,Hangup
    exten => h,1,Hangup

    After you finish up the remainder of this tutorial and try a test fax, you may need to adjust the number in the NVfaxdetect lines (s,4 or 111,8) of the above contexts. The original tutorial we read on this topic had 4 for the value which was supposed to mean 4 seconds; however, Asterisk read it as 4 milliseconds. 10 works on both of our Asterisk systems, one version 1.5 and one version 2.2. YMMV. Others have had luck deleting the number altogether leaving just the open and closed parentheses. Try different approaches until it works reliably on your system. What you're striving for is just enough time to detect a fax without putting regular callers to sleep waiting on your fax detection mechanism to complete its task.


    Whether you're using a standard Asterisk@Home 1.5 or 2.x or the Stealth AutoAttendant, you'll also need to modify the incoming fax context. Just choose the code below that matches your version of Asterisk and replace the existing content in [ext-fax]: AMP->Maintenance->Config Edit->extensions.conf->ext-fax. Don't forget to restart Asterisk after making these changes. From a command prompt while logged in as root, the following command will do it in Asterisk@Home 2.2: amportal restart. For Asterisk@Home 1.5 users, it takes two commands: amportal stop then amportal start. Then you'll be ready to start receiving junk faxes just like we do. Enjoy!

    For Asterisk@Home 1.5 systems whether you're using the Stealth AutoAttendant or not, replace the existing contents of [ext-fax] with the following code. If you cut-and-paste, be sure to replace the typographic quote marks in line h,2 with regular quotation marks or expect smoke.

    exten => s,1,Answer
    exten => s,2,Goto(in_fax,1)
    exten => in_fax,1,StopPlaytones ; you must do this or it will play ring sounds over your fax
    exten => in_fax,2,GotoIf($[${FAX_RX} = system]?3:analog_fax,1)
    exten => in_fax,3,Macro(faxreceive)
    exten => in_fax,7,Hangup
    exten => analog_fax,1,GotoIf($[${FAX_RX} = disabled]?3:2) ;if fax is disabled, just hang up
    exten => analog_fax,2,Dial(${FAX_RX},20,d)
    exten => analog_fax,3,Hangup
    exten => out_fax,1,txfax(${TXFAX_NAME}|caller)
    exten => out_fax,2,Hangup
    exten => h,1,system(tiff2ps -2eaz -w 8.5 -h 11 ${FAXFILE} | ps2pdf - ${FAXFILE}.pdf)
    exten => h,2,system(mime-construct --to ${EMAILADDR} --subject "Fax from ${CALLERIDNUM} ${CALLERIDNAME}" --attachment ${CALLERIDNUM}.pdf --type application/pdf --file ${FAXFILE}.pdf)
    exten => h,3,system(rm ${FAXFILE} ${FAXFILE}.pdf)
    exten => h,4,Hangup()

    For Asterisk@Home 2.x systems whether you're using the Stealth AutoAttendant or not, replace the existing contents of [ext-fax] with the following code. If you cut-and-paste, be sure to replace the typographic quote marks in line h,2 with regular quotation marks or expect smoke.

    exten => s,1,Answer
    exten => s,2,Goto(in_fax,1)
    exten => in_fax,1,StopPlaytones ; you must do this or it will play ring sounds over your fax
    exten => in_fax,2,GotoIf($[${FAX_RX} = system]?3:analog_fax,1)
    exten => in_fax,3,Macro(faxreceive)
    exten => in_fax,7,Hangup
    exten => analog_fax,1,GotoIf($[${FAX_RX} = disabled]?3:2) ;if fax is disabled, just hang up
    exten => analog_fax,2,DBGet(DIAL=DEVICE/${FAX_RX}/dial);
    exten => analog_fax,3,Dial(${DIAL},20,d)
    exten => analog_fax,4,Hangup
    exten => out_fax,1,txfax(${TXFAX_NAME}|caller)
    exten => out_fax,2,Hangup
    exten => h,1,system(tiff2ps -2eaz ${FAXFILE} | ps2pdf - ${FAXFILE}.pdf)
    exten => h,2,system(mime-construct --to ${EMAILADDR} --subject "Fax from ${CALLERIDNUM} ${CALLERIDNAME}" --attachment ${CALLERIDNUM}.pdf --type application/pdf --file ${FAXFILE}.pdf)
    exten => h,3,system(rm ${FAXFILE} ${FAXFILE}.pdf)
    exten => h,4,Hangup()

    R.I.P. WordPress Cut-And-Paste Nightmare. For long-time readers of this column, you know what a royal pain cutting-and-pasting has been at Nerd Vittles thanks to the WordPress blog's proclivity for changing quotation marks (used in many Asterisk commands) to 'smart quotes'. The original version of this column introduced an entirely new problem when WordPress replaced double-hyphens (used in many Linux commands) with 'long hyphens.' The end result of these quirks has been that, while the code worked great on our development systems, it blew up when you used cut-and-paste to move it to your Asterisk server. This all came to a head this past week when this article on faxing hit the street and blew up on every reader's system because of the double-hyphens (h,2 above), a problem that neither we nor anyone else in the WordPress blog community had ever documented.

    Just like everything else that's great about the Open Source community, there is always someone smart enough not only to recognize a problem but also to fix it. So our hat is forever tipped to Alex King, one of WordPress's most ardent supporters. He wrote about the smart quotes problem and then he single-handedly fixed it with his WP Unformatted Plugin. As luck would have it, the plugin also resolves the double-hyphen problem. It just took us a while to discover the plugin. But there's nothing like an extra scoop of egg on your face to make you scratch a little harder for a solution. We were either going to solve the formatting problems or give WordPress the heave-ho. Now that we've implemented the plugin, we'll be using it regularly and, as quickly as we can, we'll go back and rework all of our previous articles just as we have this one. Hopefully the problem is history both for you and for us. Should you see any Nerd Vittles code that still looks like it has quotation marks pointing in two different directions, please let us know. And ... our apologies for the mess.


    Some Recent Nerd Vittles Articles of Interest...