Can I demodulate Bell 202 FSK using a microcontroller? - Page 1
Forum section: electronics / beginners

author: Peabody
author_rank: Super Contributor
timestamp: May 13, 2022, 05:21:40 am
content: Ok, I just told you everything I know abount demodulation. I would like to demodulate Caller ID packets directly using a microcontroller. I know they make special chips that handle that (HT9032D), but I would have to get one, and they require a crystal.Caller ID uses Bell 202 modulation, which is FSK, with mark/space frequencies of 1200Hz/2200Hz, and a bit rate of 1200 bps. So I thought I could run the incoming signal through a bridge rectifier, and then for a single bit period a mark would have two highs and a space would have three or possibly four.Of course I don't know if a bit always starts at a zero crossing point, nor do I know what the waveform looks like in the transition from a space to a mark. At 2200 Hz, it wouldn't quite get back to zero when the bit period ends, and I don't know what happens then.Well, this may be a crazy idea, but since the frequencies are so low, I just wondered if it might be possible to demodulate the signal directly instead of using whatever other method would normally be used if the frequencies were much higher - PLLs or whatever.Knowing that someone will ask, it's because I live in a gated community, and visitors pull up to the gate call box, then enter a number. The box then dials my phone number, I answer and hit "9" and the gate opens. But if I'm not near a phone, it goes to voicemail, and the visitor can't get in. So I want to set up a device that wlll detect the calling number of the gate box from Caller ID, answer the phone, transmit a "9" in DTMF, then hang up. I use an Ooma home phone system, which outputs Caller ID packets to all wired extension phones, so that part is already done. I just need to listen on the line and demodulate the packets, which are sent after the first ring.

author: geggi1
author_rank: Frequent Contributor
timestamp: May 13, 2022, 06:22:06 am
content: Google HAM TNC.This is used for hamradio APRS and packet radio.

author: Peabody
author_rank: Super Contributor
timestamp: May 13, 2022, 03:37:50 pm
content: Thanks very much, evb149, for the extended response. You gave me a lot to think about. I guess at some point it becomes simpler to just buy the Caller ID chip and the crystal and be done with it. I've looked for relevant Arduino projects, and that appears to be what they all do. But I think the first step is to use Audacity to record a packet and see what the phase transitions actually look like. I already have the interface circuit which I built back in the day to record phone calls. Maybe that will reveal some really simple way to detect the specific calling number.I did find this on Github:https://github.com/jarodgers/bell202_decoderIt's written for the Atmega328P, so an Arduino Uno or Nano should work. It looks awfully complicated, but I'll study it to see if I can figure out what's going on.

author: MikeK
author_rank: Super Contributor
timestamp: May 13, 2022, 06:04:15 pm
content: As mentioned, packet radio uses Bell 202 FSK. And there are open projects online. Look for "PIC TNC", "PIC16F628 TNC" or similar. I've done it, it isn't difficult. A website by Mike something (Berg?) has several examples. I even remember helping him test one.

author: MarkL
author_rank: Unknown
timestamp: May 14, 2022, 01:04:31 am
content: This may not be as interesting as doing it yourself, but there are modems out there that support caller ID.You can watch the serial port for "RING" followed by the incoming ID, and if it matches the gate, send the modem the AT commands to pick up the phone and dial "9" to let them in.

author: Peabody
author_rank: Super Contributor
timestamp: May 14, 2022, 03:34:59 am
content: Back when I had a desktop computer, I could have added a modem card. I guess today it would be a USB modem - a voice modem with Caller ID, or I think they called them fax modems. Looks like they are about $35 new on Amazon. I don't know if I can get a used one essentially for free, or nearly free.

author: MarkL
author_rank: Unknown
timestamp: May 14, 2022, 05:30:24 am
content: A good search term would be "56k modem caller id".You could also poke around on ebay for deals and look in the user manual for those models. I have a Zoom 3048C for a legacy application and I can say for a fact it has caller ID. The Multitech MT5634ZBA also has it.The exchange would look something like this:at+vcid=1 [turn on CID]OK[wait for incoming call]RINGDATE = 0513TIME = 2348NMBR = 123456789NAME = FRONTGATERINGatz [soft reset]OKats11=255 x3 dt9; [increase tone duration, enable blind dialing, send the 9 tone]OKath [hang up]OK

author: brucehoult
author_rank: Super Contributor
timestamp: May 14, 2022, 07:13:45 am
content: Quote from: Peabody on May 13, 2022, 03:37:50 pmHere's one that looks reasonable and runs on an ATtiny10 with 1k flash and 32 bytes of SRAM!https://sites.google.com/site/wayneholder/attiny-4-5-9-10-assembly-ide-and-programmer/bell-202-1200-baud-demodulator-in-an-attiny10ATtiny85 will be overkill, let alone ATmega328.

author: gf
author_rank: Super Contributor
timestamp: May 14, 2022, 10:54:04 am
content: Here is a FSK modulator/demodulator I've written some time ago, just for fun.It is a DSP-based modem implementation. There exist simpler methods, but I don't think that they have the same noise immunity then.As is, it a assumes a DAC/ADC sample rate of ~9600 bits/s. If the sample rate is changed, then the filter coefficients need to be adapated accordingly as well.The curent filter is a 3rd order 3-tap moving average, so it could possibly be implemented in a simpler way. For flexibility it is still implemented as a generic FIR filter.The code is generic C++ and not tied to a particular microcontroller.There are hooks to send and receive samples to/from the DAC/ADC, which can be re-implemented as needed.As is, the transmitter writes DAC samples to stdout, and the receiver reads ADC samples from stdin.The UART on top of the modem is a bit unusual (16-bit words), but this can be easily changed, or replaced by a different implementation.On my notebook with an old Intel(R) Core(TM) i5-3210M CPU @ 2.50GHz, it can send and receive (simultaneously) about 4Mbit/s - that's more than 3000x the required bit rate of 1200 bits/.Don't know if an 8-bit AVR is fast enough to run it at 1200 bits/s.EDIT: For the DSP stuff, a CPU with a hardware multiplier is likely a good idea.Hereis the FSK demodulator part (w/o UART) on godbolt.org, compiled for AVR.ARM requires less instructionsfor the same.EDIT: The demodulator works according tothis principle.EDIT: Eliminated division in the code below.Code:#include <inttypes.h>#include <math.h>#include <stdio.h>#include <time.h>static const int8_t sintable = {0, 3, 6, 9, 12, 16, 19, 22, 25, 28, 31, 34, 37, 40, 43, 46,49, 51, 54, 57, 60, 63, 65, 68, 71, 73, 76, 78, 81, 83, 85, 88,90, 92, 94, 96, 98, 100, 102, 104, 106, 107, 109, 111, 112, 113, 115, 116,117, 118, 120, 121, 122, 122, 123, 124, 125, 125, 126, 126, 126, 127, 127, 127,127, 127, 127, 127, 126, 126, 126, 125, 125, 124, 123, 122, 122, 121, 120, 118,117, 116, 115, 113, 112, 111, 109, 107, 106, 104, 102, 100, 98, 96, 94, 92,90, 88, 85, 83, 81, 78, 76, 73, 71, 68, 65, 63, 60, 57, 54, 51,49, 46, 43, 40, 37, 34, 31, 28, 25, 22, 19, 16, 12, 9, 6, 3,0, -3, -6, -9, -12, -16, -19, -22, -25, -28, -31, -34, -37, -40, -43, -46,-49, -51, -54, -57, -60, -63, -65, -68, -71, -73, -76, -78, -81, -83, -85, -88,-90, -92, -94, -96, -98, -100, -102, -104, -106, -107, -109, -111, -112, -113, -115, -116,-117, -118, -120, -121, -122, -122, -123, -124, -125, -125, -126, -126, -126, -127, -127, -127,-127, -127, -127, -127, -126, -126, -126, -125, -125, -124, -123, -122, -122, -121, -120, -118,-117, -116, -115, -113, -112, -111, -109, -107, -106, -104, -102, -100, -98, -96, -94, -92,-90, -88, -85, -83, -81, -78, -76, -73, -71, -68, -65, -63, -60, -57, -54, -51,-49, -46, -43, -40, -37, -34, -31, -28, -25, -22, -19, -16, -12, -9, -6, -3};inline int8_t sin8(uint8_t phase) { return sintable; }inline int8_t cos8(uint8_t phase) { return sin8(phase + 64); }inline int16_t mul8x8_16(int8_t a, int8_t b) { return a * b; }inline int8_t mulQ7(int8_t a, int8_t b) { return a * b >> 7; }constexpr uint64_t cpuclock = 16000000;constexpr uint64_t fsdiv = 13 * 128;constexpr uint64_t fs = cpuclock / fsdiv; // ~9600 Sa/sconstexpr uint64_t samplesPerBit = 8;constexpr uint64_t f1 = 1200;constexpr uint64_t f2 = 2200;constexpr uint64_t fLo = (f1 + f2) / 2;constexpr uint32_t phaseIncr1 = (1ULL << 32) * f1 * fsdiv / cpuclock;constexpr uint32_t phaseIncr2 = (1ULL << 32) * f2 * fsdiv / cpuclock;constexpr uint32_t phaseIncrLo = (1ULL << 32) * fLo * fsdiv / cpuclock;// ================================================================================class DDS{uint32_t phaseAccumulator = 0;public:inline int8_t nextSample(uint32_t phaseIncr){uint8_t truncatedPhase = phaseAccumulator >> 24;int8_t sample = sin8(truncatedPhase);phaseAccumulator += phaseIncr;return sample;}inline void nextSample(uint32_t phaseIncr, int8_t& re, int8_t& im){uint8_t truncatedPhase = phaseAccumulator >> 24;re = cos8(truncatedPhase);im = -sin8(truncatedPhase);phaseAccumulator += phaseIncr;}};// ================================================================================template <const int8_t* fircoeff, unsigned NTAPS, unsigned SHIFT>class FIR{static constexpr uint8_t NTAPS2 = NTAPS * 2;int8_t samples;uint8_t sampleIndex = 0;public:inline void filter(int8_t& re_, int8_t& im_){constexpr uint8_t NTAPS2 = NTAPS * 2;int8_t re = re_;int8_t im = im_;// put in bufferuint8_t idx = sampleIndex;samples[idx++] = re;samples[idx++] = im;idx = (idx >= NTAPS2) ? 0 : idx; // buffer wrap aroundsampleIndex = idx;// calculate convolutionint8_t *p = &samples;int32_t accre = 0, accim = 0;const int8_t *coeff = fircoeff;for (uint8_t i = idx; i < NTAPS2; i += 2){int8_t c = *coeff++;accre += mul8x8_16(*p++, c);accim += mul8x8_16(*p++, c);}p = samples;for (uint8_t i = 0; i < idx; i += 2){int8_t c = *coeff++;accre += mul8x8_16(*p++, c);accim += mul8x8_16(*p++, c);}// scale back to approx -128...127 range and return result// workaround for ineffcient code generated by compilerre_ = int16_t(accre >> 8) >> (SHIFT-8);im_ = int16_t(accim >> 8) >> (SHIFT-8);}};// ================================================================================class UART;class FSKModem{static constexpr uint8_t NTAPS = 7;static const int8_t fircoeff;int8_t prevRe = 127;int8_t prevIm = 0;DDS localOscillator;FIR<fircoeff, NTAPS, 9> lowpass;UART *pUART = nullptr;void (*sendToDAC)(int8_t sample) = nullptr;private:friend class UART;FSKModem(UART* u, void (*dacout)(int8_t)) : pUART(u), sendToDAC(dacout) {}inline void sendBit(bool bit);void processSample(int8_t sample);};// ================================================================================class UART{enum eState { WaitForHi, WaitForStart, Collect };eState state = WaitForHi;uint8_t time;uint32_t frame;FSKModem modem;void (*rcvCallback)(uint16_t word) = nullptr;public:UART(void (*sendToDAC)(int8_t),void (*rcvCallback_)(uint16_t)): modem(this, sendToDAC), rcvCallback(rcvCallback_){}void sendWord(uint16_t word);void processAdcSample(int8_t sample) { modem.processSample(sample); }private:friend class FSKModem;inline void processSample(bool sample);};// ================================================================================const int8_t FSKModem::fircoeff = {// 3rd order 3-tap moving average18, 54, 109, 127, 109, 54, 18};void FSKModem::sendBit(bool bit){uint32_t phaseIncr = bit ? phaseIncr1 : phaseIncr2;for (uint8_t i = 0; i < samplesPerBit; i++)sendToDAC(localOscillator.nextSample(phaseIncr));}void FSKModem::processSample(int8_t sample){// quadrature down-convert center frequency to DCint8_t loRe, loIm;localOscillator.nextSample(phaseIncrLo, loRe, loIm);int8_t re = mulQ7(sample, loRe);int8_t im = mulQ7(sample, loIm);// apply lowpass filterlowpass.filter(re, im);// quadrature demodulatebool bitsample = mul8x8_16(prevIm, re) > mul8x8_16(prevRe, im);prevRe = re;prevIm = im;pUART->processSample(bitsample);}// ================================================================================void UART::sendWord(uint16_t word){modem.sendBit(false); // start bituint16_t mask = 0x8000;for (uint8_t i = 0; i < 16; i++){modem.sendBit(word & mask);mask >>= 1;}modem.sendBit(true); // 2x stop bitmodem.sendBit(true);}void UART::processSample(bool sample){if (state == Collect){time++;// start,data,stop: 0,1-16,17-18uint8_t bitnum = time / samplesPerBit;if (bitnum == 0 && sample && time < samplesPerBit - 1){// start bit framing error//fprintf(stderr, "uart: framing error 1\n");state = WaitForHi;}else if (time % samplesPerBit == samplesPerBit / 2){// sample is at center of the pulseswitch (bitnum){case 0:break;case 17:case 18:if (!sample) {// stop bit framing error//fprintf(stderr, "uart: framing error 2\n");state = WaitForHi;}if (bitnum == 18){// frame donestate = WaitForHi;rcvCallback(frame);}break;default:frame <<= 1;if (sample)frame |= 1;}}}else if (state == WaitForHi){if (sample)state = WaitForStart;}else if (state == WaitForStart){if (!sample) {time = 0;frame = 0;state = Collect;}}}// ================================================================================static void rcvCallback(uint16_t word){putchar(word);}static void sendToDAC(int8_t sample){// add noise// sample = sample * 7 / 8 + rand() % 32 - 16;sample = sample * 3 / 4 + rand() % 64 - 32;putchar(sample);}int main(int argc, char *argv[]){srand(time(NULL));UART uart(sendToDAC, rcvCallback);if (argc > 1){// sendint c;uart.sendWord(0xffff);uart.sendWord(0xffff);while ((c = getchar()) != EOF)uart.sendWord(c);}else{// receiveint c;for (int i = 0, n = rand() % 8; i < n; i++)getchar();while ((c = getchar()) != EOF)uart.processAdcSample(c);}}

author: Peabody
author_rank: Super Contributor
timestamp: May 14, 2022, 02:18:12 pm
content: Quote from: MarkL on May 14, 2022, 05:30:24 amWell, that looks like a manual session inside a terminal program. But I would need it to be automated. I was thinking maybe VBScript would work, but I don't know how to open communications with a USB modem from VBScript in Windows. Or from C for that matter. I assume it is straighforward, but need to find an example somewhere.The advantage of using a modem is that it has the interface and the relay built in. So there's no hardware to put together. And the software just boils down to a few AT commands. But I'd kinda like to get an Arduino version to work if I can. I think it would be a nifty project.

author: Peabody
author_rank: Super Contributor
timestamp: May 14, 2022, 02:33:50 pm
content: Thanks very much for the links to existing solutions. It looks like they are properly done, with high sampling rates. But the thing that intrigues me about the software I found on Github is that there isn't a single ADC operation in the code. The entire process appears to be based on pin change interrupts on INT0. I assume that means that the incoming signal has to be converted to rail-to-rail square wave, and then the process is based on the time it takes to go from one state to the other.Of course I don't know if this actually works, but I would like to test it. I have recorded a CID packet using Audacity, so I should be able to play that back into a comparator or opamp and feed the output into INT0, and see what happens.But it's comforting to know that doing it the "right" way can be carried out even on a ATTiny10.

author: MarkL
author_rank: Unknown
timestamp: May 14, 2022, 03:06:59 pm
content: Well, yes, that's a manual session to demonstrate that it can be done (and it does work; I tried it).A modem already has the telco line interface, ring detection, line isolation, surge protection, CID tone decoding, DTMF tone generation, etc. that you will need. It's a way to "get it done" in an evening, if that's your goal. If your goal is to learn about DSP and telco line interfacing, you have several good starting points already posted. But don't underestimate the telco interfacing; you can't just wire that up to a processor.The scripting would not be too complicated to automate a modem. If you want to do it on windows (or linux), there's a package called "expect" which was written for this type of work. It has statements of the form "expect this or that" and if you get this or that, respond with an appropriate string. It's an old package and probably still available for recent versions of windows:https://www.nist.gov/services-resources/software/expectIf your interest is doing it on an Arduino, I would probably shy away from using a USB modem and stick with a simple serial (UART) interface to an old modem. You don't really need to complicate things with a USB layer for this. The only additional hardware you would need is a TTL to RS232 level converter from the Arduino to the modem.It's major overkill, but I suppose you could also use an RPi with linux and "expect".

author: Peabody
author_rank: Super Contributor
timestamp: May 14, 2022, 03:30:06 pm
content: I understand about the telco interface (actually the Ooma Telo interface in my case). Years ago I made an interface so I could record phone calls, and I followed some AN guidance on how to do that. It has a transformer, a high-voltage cap, resistors, and an MOV. Anyway, it brings even ring voltage down to appropriate levels, and works quite well. I've used it to record a CID packet for this project. But it doesn't have a way to answer the phone or inject DTMF audio. So I would have to add that. But you are right of course that using a modem would greatly simplify things.

author: Peabody
author_rank: Super Contributor
timestamp: May 14, 2022, 07:35:59 pm
content: Well, MarkL, I can prove you don't have to be smart if you're lucky. A thought which kept nagging me was that at some point in the past I had a laptop that had a phone jack. Not a desktop, but a laptop. Finally this morning I went out into the garage to the pile of "recycle electronics" which I just hadn't gotten around to disposing of. And there I found my old XP Toshiba L35-S2171 laptop, and sure enough, it did have a phone jack. And I also had the power supply, battery and mouse. I had long ago wiped the drive, but found that I had kept an image of C:, which I then restored.Documentation and Device Manager said it had a "software" modem. So I fired up Hyperterminal, sent AT commands, and got OKs. But does it have caller ID? Then I sent:AT #CID=?and got back+VCID:(0-2)Things were definitely looking up. So I set VCID to 1, which is supposed to be the cooked, interpreted contents of the packet, as opposed to raw hex, connected to a nearby phone jack, and dialed my Ooma number from my cell phone.And it worked. It gave the date and time, the calling number, and the calling name. Yay!!!So now I just have to figure out a way to communicate with the software modem. I can't even find the files. Well, Device Manager says it's on COM3, so maybe that's the way to get to it. I also found a PDF containing all the AT commands for the Agere soft modem products - they apparently built the modem for Toshiba - so I can check those against your commands for answering and dialing "9". Well, if Hyperterminal can connect to the modem, there must be a way for me to do it too. That would be the last hurdle.Thanks very much continuing to gently push this option. I may still try to work it out for a microcontroller, but only to satisfy my curiosity.

author: MarkL
author_rank: Unknown
timestamp: May 14, 2022, 08:29:34 pm
content: Cool! The price was definitely right.My modem behaved a little strangely in that it wouldn't pick up the phone to dial 9 without the soft reset (atz) first. I suspect it didn't like that I was telling it to originate a call while the phone was ringing. The atz probably reset some internal state machine.I would suggest making sure you can make it work manually with the AT commands before diving into writing the script.

author: brucehoult
author_rank: Super Contributor
timestamp: May 14, 2022, 11:58:14 pm
content: Quote from: Peabody on May 14, 2022, 02:33:50 pmYou probably don't need the comparator or opamp. Digital inputs pretty much act as a comparator anyway. I've seen someone use a digital input as an ADC by feeding it both the input signal and the output of a DAC (smoothed PWM I think). Just drive the PWM output duty cycle up/down depending on whether the input is reading 1 or 0.

author: David Hess
author_rank: Super Contributor
timestamp: May 15, 2022, 09:10:54 am
content: Quote from: evb149 on May 15, 2022, 01:45:49 amAfter any gain control and signal conditioning if present, the circuit normally used is called a data slicer; it compares the input signal to an average of the input signal.

author: Peabody
author_rank: Super Contributor
timestamp: May 17, 2022, 04:33:11 am
content: I found some time to work on the code for the laptop software modem. I was going to use VBScript, but from what I found online, VBScript doesn't give you full access to COM ports. Anyway, I remembered that I had written a C program to do bootloader stuff for an MSP430, and was able to adapt that code to this project. So now it appear to do everything I want it to do. It waits for a RING, then takes in the CID text, and if the gate phone number is in that text, it goes offhook and dials 9, then hangs up. Then, whether or not it was a number match, it starts over.I'll test it tomorrow to see if it opens the gate for me. I'm not sure the "9" is loud enough, or lasts long enough, to trigger the gate controller. I'll just have to see if it works.I didn't need the ATZ before going offhook and dialing 9. And I set S93 to 5, which is the loudest the DTMF can be set. But otherwise, MarkL, I used your AT commands.I would still like to take a look at the microcontroller version when I have time.

author: MarkL
author_rank: Unknown
timestamp: May 17, 2022, 02:11:44 pm
content: If a single "9" tone is too short, you could try sending several in a row. There will be a gap between them, but maybe the gate receiver will be tolerant of it. If not, you could look for a register that lessens the gap for your modem (I don't see one on mine).

author: Peabody
author_rank: Super Contributor
timestamp: May 17, 2022, 09:25:43 pm
content: One 9 is enough. I tried it twice at the gate, and it worked like a charm both times. So I'm going to declare victory. Thanks very much to everyone for your help. If I decide to follow up on the MCU version of this, I'll be back. I'm probably the last one in the country that would set up the gate to call their land line, but I'll post the code in case anyone ever needs it. It will be obvious that I'm not fluent in C. By the way, in addition to being Jenny's number, 8675309 is a also prime number. This may mean that Jenny was prime.Code:/*This program runs under XP on a Toshiba L35-S2171 laptop with an Ageresoftware modem built in. It monitors Caller ID for the gate's phonenumber in a gated community. If there's a match, it goes offhook,then dials 9, then hangs up. This is supposed to prevent the situationwhere the visitor calls from the gate, but the call goes to voicemail,so the visitor is stuck. The LCC compiler was used. It's a console app.*/#include <stdio.h>#include <conio.h>char phonenum[] = "8008675309"; //the gate's calling numberchar comport[] = "\\\\.\\COM3"; //COM port of modemchar AT1[] = "AT+VCID=1 E0 V1 Q0\r\n"; //enable Caller IDchar *pAT1 = &AT1;char AT2[] = "ATS11=255 S93=5 X3 DT9;\r\n"; //answer and dial 9char *pAT2 = &AT2;char AT3[] = "ATH\r\n"; //hang upchar *pAT3 = &AT3;char AT4[] = "ATZ\r\n"; //reset modemchar *pAT4 = &AT4;char *ret;int Outwrote = 0;int *pOutwrote = &Outwrote;char Inbyte = 0;char *pInbyte = &Inbyte;int Fetched = 0;int *pFetched = &Fetched;int i;int j;char buf; //buffer for Caller ID messagechar *pbuf = &buf;int main(){Entry:for (i = 0; i < 250; i++) { //clear bufferbuf = 0;}HANDLE Port = CreateFile(comport, //open COM portGENERIC_READ | GENERIC_WRITE,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);if (Port == INVALID_HANDLE_VALUE) {int err = GetLastError();printf("Error opening port \r\n");return err;}else {printf("COM port Opened \r\n");}PurgeComm(Port, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);COMMTIMEOUTS timeouts = {0}; //set timeoutstimeouts.ReadIntervalTimeout = 0;timeouts.ReadTotalTimeoutConstant = 100;timeouts.ReadTotalTimeoutMultiplier = 0;timeouts.WriteTotalTimeoutConstant = 100;timeouts.WriteTotalTimeoutMultiplier = 0;SetCommTimeouts(Port, &timeouts);DCB dcbMasterInitState; //save state of COM portGetCommState(Port, &dcbMasterInitState);DCB dcbMaster = dcbMasterInitState; //make changesdcbMaster.BaudRate = 115200;dcbMaster.Parity = NOPARITY;dcbMaster.ByteSize = 8;dcbMaster.StopBits = ONESTOPBIT;SetCommState(Port, &dcbMaster);WriteFile(Port,pAT1,strlen(AT1),pOutwrote,NULL); //enable Caller IDSleep(1000);do {ReadFile(Port,pInbyte,1,pFetched,NULL); //read and dump any response}while (Fetched > 0);do { //look for "RING" message to beginSleep(500);ReadFile(Port,pInbyte,1,pFetched,NULL);printf(".");}while (Fetched == 0);Sleep(1000); //allow time for Caller ID to completeReadFile(Port,pbuf,250,pFetched,NULL); //read in Caller ID messageSleep(100);ret = strstr(buf,phonenum); //does message contain the right number?if(ret) {printf("Gate Call\r\n"); //yes - dial 9 then hang upWriteFile(Port,pAT2,strlen(AT2),pOutwrote,NULL);Sleep(5000); //minimum 3 sec delay to dial after off-hookWriteFile(Port,pAT3,strlen(AT3),pOutwrote,NULL);Sleep(500);}else printf("Not Gate\r\n"); //noWriteFile(Port,pAT4,strlen(AT4),pOutwrote,NULL); //reset everything and close COM portSleep(10000);SetCommState(Port, &dcbMasterInitState);sleep(60);CloseHandle(Port);Port = INVALID_HANDLE_VALUE;goto Entry; //start over}