Плата Raspberry Pi благодаря своим уникальным возможностям является мощным инструментом для создания разнообразных приложений. И в этой статье мы рассмотрим создание на ее основе и GSM модуле SIM800L интерактивного автоответчика (системы IVR (Interactive Voice Response) – телефонная система автоматического ответа), способного самостоятельно совершать автоматические телефонные звонки (производить обзвон клиентов при необходимости) и передавать сообщения и считывать ответ абонента в виде DTMF сигналов. Формируемый ответ в виде DTMF сигнала будет зависеть от того, какую клавишу нажал абонент на своем телефоне, когда мы ему позвонили. Дополнительно абоненту, которому мы звоним, будут воспроизводиться голосовые сообщения из заранее записанных на плате аудио файлов.
Также на нашем сайте вы можете посмотреть проект телефонного автоответчика на основе платы Arduino.
Зачем GSM модуль? Почему не делать звонки с помощью API?
Плата Raspberry Pi является достаточно мощным инструментом разработки различных систем и с помощью написанной программы на python она позволяет делать телефонные звонки программным способом, то есть используя средства API. Но тарифы существующих сейчас операторов подобной программной телефонии пока достаточно дорогие для совершения массовых звонков – необходимо платить за каждую минуту разговора, плата за входящие сообщения и звонки взимается отдельно. Если звонков мало, то это может быть оправдано, но при большом числе звонков каждый день это становится просто разорительно. К тому же необходимо отдельно оплачивать аренду телефонного номера – по умолчанию он виртуальный, а если вы хотите постоянный номер, то за это также может взиматься дополнительная плата.
Поэтому автор данного проекта (ссылка на оригинал приведена в конце статьи) выяснил, что самым дешевым сейчас способом совершения телефонных звонков с помощью платы Raspberry Pi является использование GSM модуля, например, SIM800L. С помощью него мы можем совершать телефонные звонки, используя существующие сети сотовой связи. К тому же можно выбрать удобный для себя тариф, включающий в себя заранее оплаченное количество минут и SMS. В связи с этим в данной статье мы рассмотрим подключение GSM модуля SIM800L к плате Raspberry Pi и с его помощью будем совершать автоматические телефонные звонки и передавать сообщения (SMS) в автоматическом режиме.
Какой GSM модуль лучше всего использовать для интерактивного автоответчика
Сейчас на рынке продаются модули сотовой связи для связи в сетях 4G и 5G, которые имеют весьма впечатляющие возможности. Но автор проекта стремился сделать его максимально простым в изготовлении, поэтому и GSM модуль он стремился выбрать попроще. Поэтому он выбирал между модулями Ai-thinker A9 и SIM800L. Достоинством модуля Ai-thinker A9 является его очень низкое энергопотребление, но его существенными недостатками для данного проекта является то, что он не поддерживает обнаружение DTMF сигналов и его встроенный аудио вход работает не достаточно удовлетворительно. В связи с этим автор проекта выбрал GSM модуль SIM800L, который хоть и отличается "гигантским" энергопотреблением, но зато имеет отличный встроенный аудио вход чтобы автоматически воспроизводить голос и имеет встроенную поддержку обнаружения DTMF сигналов – нам это понадобится чтобы определять какую клавишу на телефоне нажал пользователь. Но главным недостатком этого модуля является, конечно же, энергопотребление, мы это еще обсудим далее в статье.
Необходимые компоненты
- Плата Raspberry Pi (купить на AliExpress).
- GSM модуль SIM800L (купить на AliExpress).
- SIM карта с поддержкой 2G.
- AUX кабель.
- LM2596 Buck Converter Module (понижающий конвертер) (купить на AliExpress).
- Конвертер USB to TTL.
- Адаптер питания 12V 2A.
- Перфорированная плата.
- Соединительные колодки.
- Соединительные провода.
- Набор для пайки.
В качестве операционной системы для Raspberry Pi автор проекта использовал Buster OS, но подойдут и другие операционные системы.
Примечание: для данного проекта интерактивного автоответчика плата Raspberry Pi используется только для исполнения кода программы на Python и обеспечения аудио выхода, вместо нее в данном проекте можно использовать обычный компьютер.
Схема проекта
Схема интерактивного автоответчика на основе платы Raspberry Pi и модуля SIM800L представлена на следующем рисунке.
Подача питания на модуль SIM800L: одна из главных составляющих успешной работы данного проекта – это обеспечение достаточного питания для модуля SIM800L. Напряжение питание для данного модуля может составлять от 3.7V до 4.2V – хорошо подходит для питания от литий-полимерных батарей (Li-po batteries). Оптимальное напряжение питания для модуля составляет примерно 4V. В схеме мы использовали понижающий конвертер (Buck converter) LM2596 для преобразования напряжения 12V 2A с выхода адаптера питания в напряжение питания 4V, которое требуется модулю SIM800L. Также убедитесь в том, что вы используете достаточно толстые и надежные провода для соединения понижающего конвертера и модуля SIM800L (чтобы по ним мог протекать достаточно большой ток).
Если питание будет недостаточно или соединения в этом месте будут плохие, то модуль SIM800L может самопроизвольно сбрасываться и передавать неправильные данные по последовательной связи. Поэтому убедитесь в том, что модуль SIM800L подключен к "правильному" адаптеру 12V 2A достаточно толстыми проводами с небольшим сопротивлением.
Последовательная связь между модулем SIM800L и платой Raspberry Pi: если вы посмотрите предыдущие проекты на нашем сайте с использованием GSM модулей, то вы увидите, что для взаимодействия с данными модулями (в том числе и с SIM800L) необходимы так называемые AT команды. Данные команды используются для совершения/приема звонков, передачи/приема сообщений, обнаружения нажатия клавиш и т.д. В данном проекте мы будем формировать AT команды с помощью языка Python на Raspberry Pi. Для этого в нашем проекте используется конвертер USB to TTL – чтобы подключить контакты Rx и TX модуля SIM800L к USB порту платы Raspberry Pi.
Аудио вход модуля SIM800L от платы Raspberry Pi: модуль SIM800L имеет в своем контакте микрофонный вход – контакты MIC+ и MIC- на модуле. При совершении звонка любой аудио сигнал, подаваемый на эти контакты, будет воспроизводиться в телефоне корреспондента, которому мы звоним. Обычно к этим контактам подключается микрофон, но в нашем проекте нам необходимо воспроизводить заранее записанный голос с платы Raspberry Pi. Поэтому мы будем подключать к разъему 3.5mm jack платы аудио кабель, нам необходимо конвертировать этот сигнал в аудио сигнал микрофонного уровня чтобы иметь возможность принимать его на GSM. Для этой цели можно использовать какое-нибудь профессиональное устройство, применяемое в звукозаписи, но автор проекта соединил эти контакты непосредственным образом и просто уменьшил уровень звука в плате Raspberry Pi на 2 единицы – он на практике проверил, что данное решение работает вполне хорошо.
Мы собрали этот проект на перфорированной плате и перед подачей на нее питания убедились, что провода питания достаточно толстые и обладаю небольшим сопротивлением. Внешний вид собранной конструкции проекта показан на следующих рисунках.
Примечание: напряжение на выходе понижающего конвертера LM2596 может регулироваться в определенных пределах, поэтому прежде чем подключать к нему модуль SIM800L отрегулируйте напряжение на выходе конвертера LM2596 с помощью встроенного в него потенциометра до величины 4V, иначе, если напряжение на его выходе окажется более чем 4.2V, это может необратимо повредить модуль LM2596.
Если вы дошли до этого момента, то подайте питание на конструкцию проекта и вставьте SIM карту в модуль. Убедитесь в том, что вы вставили SIM карту в правильной ориентации и корректно закрепили антенну модуля. Если все работает так, как надо, то встроенный светодиод на модуле SIM800L должен начать мигать каждые 3 секунды. Это будет означать что модуль SIM800L готов к установлению соединения с сетью.
Объяснение программы для Raspberry Pi
Полный код программы приведен в конце статьи, здесь же мы кратко рассмотрим его основные фрагменты.
Первым делом в программе необходимо подключить используемые библиотеки. Мы будем использовать пакет serial для последовательной связи между платой Raspberry Pi и модулем SIM800L, пакет pygame будет использоваться для воспроизведения музыки (в нашем случае аудио файлов). Также мы будем использовать пакет time для организации временных задержек в программе. Если вы используете Buster OS, то все эти пакеты в ней уже установлены – ничего дополнительно скачивать не нужно.
1 2 3 |
import serial #for serial communication with GSM SIM800L import time import pygame #to play music |
def SIM800 (Command) – эта функция будет использоваться для передачи AT команд модулю SIM800L и получения на них ответа. Все AT команды, передаваемые модулю SIM800L, должны заканчиваться на “\r\n” и должны быть в формате ASCII. Данная функция будет добавлять ко всем нашим AT командам окончание “\r\n” и декодировать их в формат ASCII для их дальнейшего использования в нашей программе.
1 2 3 4 5 6 7 8 9 10 11 12 |
#Speak with SIM800 -> gets AT command return as response def SIM800(command): AT_command = command + "\r\n" ser.write(str(AT_command).encode('ascii')) time.sleep(1) if ser.inWaiting() > 0: echo = ser.readline() #waste the echo response_byte = ser.readline() response_str = response_byte.decode('ascii') return (response_str) else: return ("ERROR") |
def wait_for_SIM800() – эта функция очень похожа на выше описанную функцию def SIM800, но она не передает никаких значений модулю SIM800, она просто ждет от SIM800L какого-нибудь ответа и как только она его получит, она возвращает его в виде результата.
1 2 3 4 5 6 |
#checks if SIM800L is speaking and returns it as response def wait_for_SIM800(): echo = ser.readline() # waste the echo response_byte = ser.readline() response_str = response_byte.decode('ascii') return (response_str) |
def Init_GSM() – функция для инициализации GSM модуля. Сначала она проверяет его передавая ему команду “AT”, после чего она ждет от него ответа “OK”, затем она передает AT команды модулю на перевод его в режим передачи сообщений и режим приема DTMF сигналов. Также он отключает все уведомления чтобы нас не беспокоили различные текстовые сообщения во время совершения звонка.
1 2 3 4 5 6 7 |
#Checks SIM800L status and connects with ShopifyAPI def Init_GSM(): if "OK" in SIM800("AT"): if ("OK" in (SIM800("AT+DDET=1"))) and ("OK" in (SIM800("AT+CNMI =0,0,0,0,0"))) and ("OK" in (SIM800("AT+CMGF=1"))) and ("OK" in (SIM800("AT+CSMP=17,167,0,0"))): # enble DTMF / disable notifications print("SIM800 Module -> Active and Ready") else: print("------->ERROR -> SIM800 Module not found") |
def play_wav (file_name) – эта функция используется для воспроизведения wav файлов когда на наш звонок ответили. Для этих целей мы заранее сохранили на плате файлы “intro.wav”, “confirm.wav”, “cancel.wav” и т.д. Мы будем воспроизводить один из этих файлов в зависимости от того, какую кнопку нажмет на телефоне абонент, которому мы звоним. Эту функцию play_wav вы можете использовать для воспроизведения любых wav файлов. Убедитесь в том, что эти wav файлы сохранены в том же каталоге, в котором находится ваша программа на Python.
1 2 3 4 5 6 7 |
#plays the given wav file #8000Mhz mono audio WAV works best on SIM800L def play_wav(file_name): pygame.mixer.init(8000) pygame.mixer.music.load(file_name) pygame.mixer.music.play() #while pygame.mixer.music.get_busy() == True: #continue |
def Call_response_for (phone_number) – это самая важная функция в нашей программе. Она получает телефонный номер, на который необходимо позвонить и обеспечивает ответ на этот звонок. Ответы могут быть следующие: NOT_REACHABLE (абонент не доступен), CALL_REJECTED (звонок отклонен), CONFIRMED (подтверждено), CANCELED (отменено) и т.д. Функция использует AT команды для совершения звонка на необходимый номер и воспроизводит записанный голос. Затем она проверяет DTMF ответ от абонента (которому мы позвонили) и в зависимости от этого ответа она воспроизводит соответствующую аудио запись, и в завершение она сообщает нам что выбрал абонент (какую клавишу он нажал). Если абонент не доступен или он отклонил звонок, она также сообщает об этом.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
# Makes a call to given number and returns NONE, NOT_REACHABLE, CALL_REJECTED, REJECTED_AFTER_ANSWERING, REQ_CALLBACK,CANCELED, CONFIRMED def Call_response_for (phone_number): AT_call = "ATD" + phone_number + ";" response = "NONE" time.sleep(1) ser.flushInput() #clear serial data in buffer if any if ("OK" in (SIM800(AT_call))) and (",2," in (wait_for_SIM800())) and (",3," in (wait_for_SIM800())): print("RINGING...->", phone_number) call_status = wait_for_SIM800() if "1,0,0,0,0" in call_status: print("**ANSWERED**") ser.flushInput() play_wav("intro.wav") time.sleep(0.5) dtmf_response = "start_over" while dtmf_response == "start_over": play_wav("press_request.wav") time.sleep(1) dtmf_response = wait_for_SIM800() if "+DTMF: 1" in dtmf_response: play_wav("confirmed.wav") response = "CONFIRMED" hang = SIM800("ATH") break if "+DTMF: 2" in dtmf_response: play_wav("canceled.wav") response = "CANCELED" hang = SIM800("ATH") break if "+DTMF: 9" in dtmf_response: play_wav("callback_response.wav") response = "REQ_CALLBACK" hang = SIM800("ATH") break if "+DTMF: 0" in dtmf_response: dtmf_response = "start_over" continue if "+DTMF: " in dtmf_response: play_wav("invalid_input.wav") dtmf_response = "start_over" continue else: response = "REJECTED_AFTER_ANSWERING" break else: #print("REJECTED") response = "CALL_REJECTED" hang = SIM800("ATH") time.sleep(1) #ser.flushInput() else: #print("NOT_REACHABLE") response = "NOT_REACHABLE" hang = SIM800("ATH") time.sleep(1) #ser.flushInput() ser.flushInput() return (response) |
def send_message (message, recipient) – кроме совершения звонков и получения на них ответа наша программа также будет позволять нам передавать сообщения (SMS). И эта функция будет использоваться для передачи сообщений. На вход она получает сообщение и номер абонента, после чего она передает это сообщение по данному номеру.
1 2 3 4 5 6 7 8 9 10 11 |
#Receives the message and phone number and send that message to that phone number def send_message(message, recipient): ser.write(b'AT+CMGS="' + recipient.encode() + b'"\r') time.sleep(0.5) ser.write(message.encode() + b"\r") time.sleep(0.5) ser.write(bytes([26])) time.sleep(0.5) print ("Message sent to customer") time.sleep(2) ser.flushInput() # clear serial data in buffer if any |
def incoming_call() – эта функция используется для определения номера входящего звонка. Поскольку в нашем проекте мы можем делать звонки незнакомым нам людям (SIM карта нам это позволяет), то некоторые из них могут нам потом перезванивать на этот номер. В этом случае функция используется для проверки номера, с которого поступает входящий звонок и затем передачи ему сообщения или совершения ответного звонка.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
def incoming_call(): while ser.in_waiting: #if I have something in the serial monitor print ("%%Wait got something in the buffer") ser.flushInput() response = SIM800("ATH") #cut the incoming call if "+CLCC" in response: cus_phone = response[21:31] print("%%Incoming Phone call detect from ->", cus_phone) return (cus_phone) else: print("Nope its something else") return "0" return "0" |
Теперь, когда код всех необходимых нам функций написан, можно приступать к написанию основного кода программы. В качестве имени абонента, которому мы звоним, мы выбрали “AISHA”, а в качестве его номера мы для теста использовали “9877XXXXXX”. При необходимости эти данные можно считывать в программу с помощью Shopify API или из электронной таблицы.
1 2 |
cus_name = "Aisha" cus_phone = "968837XXXX" |
Внутри бесконечного основного цикла программы мы инициализируем последовательную связь со скоростью 9600 бод и таймаутом 15 секунд. Но учтите, что некоторые модули SIM800L могут работать на других скоростях, поэтому убедитесь в том, что в программе вы ввели правильную скорость и правильный номер COM каталога.
1 2 3 |
# COM defanition for windows -> Should change for Pi ser = serial.Serial("/dev/ttyUSB0", baudrate=9600, timeout=15) # timeout affects call duration and waiting for response currently 30sec print("Established communication with", ser.name) |
Далее мы будем совершать телефонный звонок и получать на него ответ от абонента, которому мы звоним. В зависимости от этого ответа мы будем передавать сообщение абоненту.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
print("_____________________IVR START___________________") response = Call_response_for(cus_phone) #place a call and get response from customer print ("Response from customer => ", response) if response == "CONFIRMED": text_message = "Hi " + cus_name + ". Your booking has been confirmed.Thank you. -Circuitdigest" send_message(text_message, cus_phone) if response == "CANCELED": # if the response was to cancel text_message = "Hi " + cus_name + ". Sorry that you have decided to cancel your booking. If you cancled by mistake, kindly contact us through phone. -Circuitdigest" send_message(text_message, cus_phone) if ((response == "CALL_REJECTED") or (response == "REJECTED_AFTER_ANSWERING")): # if the response was rejected text_message = "Hi " + cus_name + ". We from circuitdigest.com have been trying to reach you, to confirm your booking. You will receive another call within few minutes, we kindly request you to answer it. Thank you" send_message(text_message, cus_phone) print("_____________________IVR END___________________") |
Тестирование работы автоответчика
Соберите схему проекта, приведенную выше в статье, убедитесь в подаче корректного напряжения питания на плату Raspberry Pi и GSM модуль.
Перед запуском программы убедитесь в том, что у вас в коде программы указан правильный COM порт – в нашем случае это “/dev/ttyUSB0”. Затем убедитесь в том, что у вас аудио сигнал с платы будет транслироваться на AV jack – это можно сделать, сделав клик правой кнопкой мыши на иконке громкоговорителя (speaker icon). Также убедитесь в том, что уровень звука установлен на low.
На следующем шаге измените телефонный номер и имя абонента по своему выбору или внесите соответствующие изменения в программу чтобы она считывала эту информацию из excel или какого-нибудь облачного сервиса.
Во время работы программа будет совершать телефонный звонок на заданный номер и получать dtmf ответ от абонента, которому мы позвонили. Несколько образцов подобных сообщений мы привели на следующем рисунке.
Вы можете изменить аудиофайлы, используемые в проекте, на свои. Также вы можете изменить тексты всех передаваемых сообщений на свой выбор. Более подробно работу проекта вы можете посмотреть на видео, приведенном в конце статьи.
Исходный код программы на Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
import serial # для последовательной связи с GSM модулем SIM800L import time import pygame # для воспроизведения аудио файлов # _____________________________________________________________________________# # Intro text (приветственный текст) print("Setting up Raspberry PI IVR") #Speak with SIM800 -> gets AT command return as response def SIM800(command): AT_command = command + "\r\n" ser.write(str(AT_command).encode('ascii')) time.sleep(1) if ser.inWaiting() > 0: echo = ser.readline() #waste the echo response_byte = ser.readline() response_str = response_byte.decode('ascii') return (response_str) else: return ("ERROR") # поверяем отвечает ли нам SIM800L и возвращаем его ответ def wait_for_SIM800(): echo = ser.readline() # waste the echo response_byte = ser.readline() response_str = response_byte.decode('ascii') return (response_str) # проверяем состояние SIM800L и соединяем его с ShopifyAPI def Init_GSM(): if "OK" in SIM800("AT"): if ("OK" in (SIM800("AT+DDET=1"))) and ("OK" in (SIM800("AT+CNMI =0,0,0,0,0"))) and ("OK" in (SIM800("AT+CMGF=1"))) and ("OK" in (SIM800("AT+CSMP=17,167,0,0"))): # enble DTMF / disable notifications print("SIM800 Module -> Active and Ready") else: print("------->ERROR -> SIM800 Module not found") #plays the given wav file #8000Mhz mono audio WAV works best on SIM800L def play_wav(file_name): pygame.mixer.init(8000) pygame.mixer.music.load(file_name) pygame.mixer.music.play() #while pygame.mixer.music.get_busy() == True: #continue # совершаем звонок на выбранный номер и возвращаем NONE, NOT_REACHABLE, CALL_REJECTED, REJECTED_AFTER_ANSWERING, REQ_CALLBACK,CANCELED, CONFIRMED def Call_response_for (phone_number): AT_call = "ATD" + phone_number + ";" response = "NONE" time.sleep(1) ser.flushInput() #clear serial data in buffer if any if ("OK" in (SIM800(AT_call))) and (",2," in (wait_for_SIM800())) and (",3," in (wait_for_SIM800())): print("RINGING...->", phone_number) call_status = wait_for_SIM800() if "1,0,0,0,0" in call_status: print("**ANSWERED**") ser.flushInput() play_wav("intro.wav") time.sleep(0.5) dtmf_response = "start_over" while dtmf_response == "start_over": play_wav("press_request.wav") time.sleep(1) dtmf_response = wait_for_SIM800() if "+DTMF: 1" in dtmf_response: play_wav("confirmed.wav") response = "CONFIRMED" hang = SIM800("ATH") break if "+DTMF: 2" in dtmf_response: play_wav("canceled.wav") response = "CANCELED" hang = SIM800("ATH") break if "+DTMF: 9" in dtmf_response: play_wav("callback_response.wav") response = "REQ_CALLBACK" hang = SIM800("ATH") break if "+DTMF: 0" in dtmf_response: dtmf_response = "start_over" continue if "+DTMF: " in dtmf_response: play_wav("invalid_input.wav") dtmf_response = "start_over" continue else: response = "REJECTED_AFTER_ANSWERING" break else: #print("REJECTED") response = "CALL_REJECTED" hang = SIM800("ATH") time.sleep(1) #ser.flushInput() else: #print("NOT_REACHABLE") response = "NOT_REACHABLE" hang = SIM800("ATH") time.sleep(1) #ser.flushInput() ser.flushInput() return (response) #Receives the message and phone number and send that message to that phone number def send_message(message, recipient): ser.write(b'AT+CMGS="' + recipient.encode() + b'"\r') time.sleep(0.5) ser.write(message.encode() + b"\r") time.sleep(0.5) ser.write(bytes([26])) time.sleep(0.5) print ("Message sent to customer") time.sleep(2) ser.flushInput() # clear serial data in buffer if any def incoming_call(): while ser.in_waiting: #если какая нибудь информация есть в последовательном мониторе print ("%%Wait got something in the buffer") ser.flushInput() response = SIM800("ATH") #cut the incoming call if "+CLCC" in response: cus_phone = response[21:31] print("%%Incoming Phone call detect from ->", cus_phone) return (cus_phone) else: print("Nope its something else") return "0" return "0" cus_name = "Aisha" cus_phone = "96883XXXXX" while (1): #Infinite loop # COM defanition for windows -> Should change for Pi ser = serial.Serial("/dev/ttyUSB0", baudrate=9600, timeout=15) # timeout affects call duration and waiting for response currently 30sec print("Established communication with", ser.name) Init_GSM() # проверяем доступен ли GSM и инициализируем его print("_____________________IVR START___________________") response = Call_response_for(cus_phone) #place a call and get response from customer print ("Response from customer => ", response) if response == "CONFIRMED": text_message = "Hi " + cus_name + ". Your booking has been confirmed. Thank you!!. -Circuitdigest" send_message(text_message, cus_phone) if response == "CANCELED": # если ответом является отмена звонка text_message = "Hi " + cus_name + ". Sorry that you have decided to cancel your booking. If you cancled by mistake, kindly contact us through phone. -Circuitdigest" send_message(text_message, cus_phone) if ((response == "CALL_REJECTED") or (response == "REJECTED_AFTER_ANSWERING")): # если вызов был отклонен text_message = "Hi " + cus_name + ". We from circuitdigest.com have been trying to reach you, to confirm your booking. You will receive another call within few minutes, we kindly request you to answer it. Thank you" send_message(text_message, cus_phone) print("_____________________IVR END___________________") ser.close() time.sleep (5) |