Мой соавтор — DeepSeek. autoit.. autoit. gsm.. autoit. gsm. lua.. autoit. gsm. lua. искусственный интеллект.

Эта статья о моем опыте сотрудничества с DeepSeek в разработке некоторых поделок на различных языках программирования. Раньше писал на этих языках, но без помощи ИИ.

Поделка 1:

Решил разработать систему авторизованного доступа в здание с использованием телефона.

Для этого собрал модем , состоящий из 3-х деталей: адаптер USB-UART, модуль SIM800L и корпус.

Получилось вот такое устройство:Внеш

Внешний вид модема в сравнении с банковской картой

Внешний вид модема в сравнении с банковской картой

Программу решил написать на Lua.

Для этого, не в первый раз, обращаюсь к DeepSeek(DS) с просьбой разработать скрипт, который будет выполнять следующие функции:

1)      Подключаться через виртуальный com порт к модему;

2)      Контролировать подключение к сети оператора связи;

3)      Проверять уровень сигнала GSM;

4)      При звонках:

4.1), определять номер звонящего;

4.2) записывать номер в файл регистрации и в таблицу;

5)      При SMS:

5.1) Принимать SMS содержащие команды и параметры. Формат команды есть в тексте скрипта.

 Далее процесс свелся к следующему.

DS пишет скрипт.

Я его запускаю и сообщаю ему результат.

Если есть ошибки, то DS объясняет их и исправляет.

Если у меня есть идея, как изменить или расширить функции кода, то сообщаю об этом DS. 

Не всегда DS соглашается с моими идеями. В этом случае он объясняет, почему нет смысла в реализации этой идеи. 

Если же нахожу его ошибки и указываю на них, то он соглашается и исправляет.

Когда DS исправляет ошибки, то он начинает улучшать и расширять скрипт. Порою это приводит к тому, что перестают работать те функции, которые работали раньше. В этом случае, предлагаю ему вернуться к предыдущему варианту.

В результате совместной аппаратно-программной разработке, получили работающее решение за 1 день.

Результирующий код:
-- GSM модем контроллер - КОМАНДЫ С БУКВЕННЫМИ ПАРАМЕТРАМИ
-- Формат: "КОМАНДА[БУКВА][ЗНАЧЕНИЕ]..."
-- Примеры:
--   "1"                    - команда 1 без параметров
--   "1t235"                - команда 1 с параметром t=235
--   "2r1"                  - команда 2 с параметром r=1
--   "3t235h67"             - команда 3 с параметрами t=235 и h=67
--   "1t235,2r1,3t235h67"   - несколько команд через разделитель

os.execute("chcp 65001 > nul")

local port = nil
local buffer = ""

-- Таблицы для хранения данных
local calls = {}              -- входящие звонки
local incoming_commands = {}   -- входящие команды из SMS
local outgoing_messages = {}   -- отправленные SMS


-- Функция задержки для Windows
function sleep(seconds)
    local start = os.clock()
    while os.clock() - start < seconds do
        -- просто ждем
    end
end

-- Основная функция отправки команд
function sendCommand(cmd, timeout)
    timeout = timeout or 3

    -- Очищаем входной буфер
    local flush = ""
    repeat
        flush = port:read(1)
    until not flush

    -- Отправляем команду
    port:write(cmd .. "rn")
    port:flush()

    -- Собираем ответ
    local response = ""
    local start = os.clock()
    local lastDataTime = start

    while os.clock() - start < timeout do
        -- Пробуем прочитать символ
        local char = port:read(1)
        if char then
            response = response .. char
            lastDataTime = os.clock()

            -- Для отладки (можно закомментировать)
            -- print(string.format("0x%02X (%s)", char:byte(), char))

            -- Проверяем окончание ответа
            if response:find("OKrn") or
               response:find("ERRORrn") or
               response:find("NO CARRIERrn") or
               response:find("RINGrn") then
                break
            end
        else
            -- Если нет данных и прошло больше 0.2 сек после последних данных
            if #response > 0 and os.clock() - lastDataTime > 0.2 then
                break
            end
        end
    end

    return response
end

-- Функция для отправки команды спящему модему
function sendToSleepingModem(cmd, timeout)
    timeout = timeout or 3

    -- ШАГ 1: Отправляем пустую строку или AT для пробуждения
    port:write("rn")  -- Просто перевод строки для пробуждения
    port:flush()

    -- Ждем 300 мс (используем нашу функцию sleep)
    local wakeStart = os.clock()
    while os.clock() - wakeStart < 0.3 do
        -- просто ждем
    end

    -- ШАГ 2: Очищаем буфер от мусора пробуждения
    local garbage = ""
    local cleanStart = os.clock()
    while os.clock() - cleanStart < 0.2 do
        local char = port:read(1)
        if char then
            garbage = garbage .. char
        else
            break
        end
    end

    if #garbage > 0 then
        print("Пробуждение мусор:", garbage)
    end

    -- ШАГ 3: Отправляем реальную команду
    return sendCommand(cmd, timeout)
end

-- Упрощенная версия для вашего случая
function wakeAndSend(cmd, timeout)
    timeout = timeout or 3

    -- Отправляем первую команду (она разбудит модем, но ответа не будет)
    port:write("ATrn")
    port:flush()

    -- Ждем 500 мс на пробуждение
    local t = os.clock()
    while os.clock() - t < 0.5 do end

    -- Теперь отправляем реальную команду
    return sendCommand(cmd, timeout)
end

-- Открытие порта
local com = "com4"

function openPort()
    for attempt = 1, 3 do
        os.execute('mode ' .. com .. ': baud=115200 parity=n data=8 stop=1')
        local portPath = "\\.\" .. com
        port = io.open(portPath, "w+b")
        if port then
            port:setvbuf("no")
            print("Порт открыт")
            return true
        end
        print("Попытка " .. attempt .. " не удалась, ждем 2 сек...")
        sleep(2000)
    end
    print("Ошибка: не удалось открыть порт")
    return false
end

function sleep(ms)
    local start = os.clock()
    while os.clock() - start < ms / 1000 do end
end

function wrcmd(cmd)
port:write(cmd .. "rn") port:flush()
end
-- Отправка команды
function sendCommand(cmd, timeout)
    timeout = timeout or 2
    port:write(cmd .. "rn");-- port:flush()
    local response = ""
    local start = os.clock()
    while os.clock() - start < timeout do
        local char = port:read(1)
        if char then
            response = response .. char
            if response:find("OKrn") or response:find("ERRORrn") then break end
        end
    end
    return response
end

-- Очистка номера телефона
function cleanPhoneNumber(phone)
    if not phone then return nil end
    local cleaned = phone:gsub("[^%+%d]", "")
    if cleaned:match("^8%d%d%d") then
        cleaned = "+7" .. cleaned:sub(2)
    end
    if #cleaned >= 10 and #cleaned <= 15 then
        return cleaned
    end
    return nil
end

-- Извлечение номера из строки CLIP
function extractPhoneFromCLIP(line)
    local phone = line:match('CLIP: "([^"]+)"')
    if phone then
        return cleanPhoneNumber(phone)
    end
    return nil
end

-- ============================================
-- ПАРСИНГ КОМАНД С БУКВЕННЫМИ ПАРАМЕТРАМИ
-- ============================================

-- Парсинг одной команды с буквенными параметрами
function parseSingleCommand(cmd_str)
    if not cmd_str or cmd_str == "" then return nil end

    -- Ищем команду (первые цифры)
    local cmd_num = cmd_str:match("^(%d+)")
    if not cmd_num then return nil end

    local cmd = tonumber(cmd_num)
    local rest = cmd_str:sub(#cmd_num + 1)

    local params = {}

    -- Разбираем буквенные параметры
    if rest and #rest > 0 then
        -- Ищем все паттерны "буква + число"
        for letter, value in rest:gmatch("([a-zA-Z])(%d+)") do
            params[letter] = tonumber(value)
        end
    end

    return {
        cmd = cmd,
        params = params,
        raw = cmd_str
    }
end

-- Разбор SMS с несколькими командами
function parseCommands(text)
    if not text or text == "" then return {} end

    local commands = {}

    -- Разделяем по любым не-цифробуквенным символам
    -- (запятые, пробелы, точки с запятой и т.д.)
    for part in text:gmatch("[^,%s;:]+") do
        if part ~= "" then
            local cmd_data = parseSingleCommand(part)
            if cmd_data then
                table.insert(commands, cmd_data)
            end
        end
    end

    return commands
end

-- Вывод команды в читаемом виде
function dumpCommand(cmd_data)
    local str = string.format("Команда %d", cmd_data.cmd)
    if next(cmd_data.params) then
        local params = {}
        for letter, value in pairs(cmd_data.params) do
            table.insert(params, string.format("%s=%d", letter, value))
        end
        str = str .. " [" .. table.concat(params, ", ") .. "]"
    end
    return str
end

-- Обработка нескольких команд
function processCommands(phone, commands)
    print(string.format("  Получено %d команд", #commands))

    local responses = {}

    for idx, cmd_data in ipairs(commands) do
        print("  " .. dumpCommand(cmd_data))

        local response = nil

        -- Команда 1: запрос статуса
        if cmd_data.cmd == 1 then
            response = "101"

        -- Команда 2: управление реле
        elseif cmd_data.cmd == 2 then
            if cmd_data.params['r'] then
                if cmd_data.params['r'] == 1 then
                    relayOn()
                    response = "102r1"
                elseif cmd_data.params['r'] == 0 then
                    relayOff()
                    response = "102r0"
                end
            end

        -- Команда 3: данные с датчиков
        elseif cmd_data.cmd == 3 then
            local temp = readTemperature()
            local humidity = readHumidity()
            local pressure = readPressure()

            -- Формируем ответ с буквенными параметрами
            response = "103"
            if temp then response = response .. "t" .. temp end
            if humidity then response = response .. "h" .. humidity end
            if pressure then response = response .. "p" .. pressure end

        -- Команда 4: установка пара��етров
        elseif cmd_data.cmd == 4 then
            if cmd_data.params['t'] then
                setTimer(cmd_data.params['t'])
            end
            if cmd_data.params['d'] then
                setDelay(cmd_data.params['d'])
            end
            response = "104"
            if cmd_data.params['t'] then response = response .. "t" .. cmd_data.params['t'] end
            if cmd_data.params['d'] then response = response .. "d" .. cmd_data.params['d'] end

        -- Команда 5: мульти-запрос
        elseif cmd_data.cmd == 5 then
            local temp = readTemperature()
            local status = getSystemStatus()
            response = "105t" .. temp .. "s" .. status
        end

        if response then
            table.insert(responses, response)
        end
    end

    -- Отправляем все ответы одним SMS
    if #responses > 0 then
        local answer = table.concat(responses, ",")
        sendSMS(phone, answer)
    end
end

-- SMS ФУНКЦИИ
-- Отправка SMS
function sendSMS(phone, text)
    phone = cleanPhoneNumber(phone)
    if not phone then
        print("Ошибка: неверный формат номера")
        return false
    end

    print("Отправка SMS на " .. phone .. ": " .. text)

    sendCommand("AT+CMGF=1", 1)
    sleep(100)

    local resp = sendCommand('AT+CMGS="' .. phone .. '"', 2)

    if resp:find(">") then
        port:write(text .. "26")
        port:flush()

        sleep(2000)
        local final_resp = ""
        local start = os.clock()
        while os.clock() - start < 5 do
            local char = port:read(1)
            if char then
                final_resp = final_resp .. char
                if final_resp:find("OK") or final_resp:find("ERROR") then
                    break
                end
            end
        end

        if final_resp:find("OK") then
            print("? SMS отправлена")

            -- Сохраняем в историю
            local msg = {
                id = #outgoing_messages + 1,
                phone = phone,
                text = text,
                date = os.date("%Y-%m-%d"),
                time = os.date("%H:%M:%S"),
                timestamp = os.time()
            }
            table.insert(outgoing_messages, msg)

            -- Лог
            local today = os.date("%Y-%m-%d")
            local f = io.open("sent_sms_" .. today .. ".log", "a")
            if f then
                f:write(string.format("[%s] КОМУ: %sn", os.date("%H:%M:%S"), phone))
                f:write("    ТЕКСТ: " .. text .. "n")
                f:write("    " .. string.rep("-", 50) .. "n")
                f:close()
            end

            saveToFile()
            return true
        else
            print("Ошибка отправки: " .. final_resp)
            return false
        end
    else
        print("Ошибка: модем не ответил символом >")
        return false
    end
end

-- Чтение SMS
function readSMS(index)
    local resp = sendCommand('AT+CMGR=' .. index, 3)

    local lines = {}
    for line in resp:gmatch("[^rn]+") do
        table.insert(lines, line)
    end

    if #lines < 2 then
        return nil, nil
    end

    local header = lines[1]
    local phone = header:match('"+([%+%d]+)"')
    local date = header:match(',,"([^"]+)"') or header:match('","([^"]+)"$')

    local text = ""
    for i = 2, #lines do
        if lines[i] ~= "OK" and lines[i] ~= "" then
            text = lines[i]
            break
        end
    end

    return phone, text, date
end

-- Удаление SMS
function deleteSMS(index)
    sendCommand('AT+CMGD=' .. index, 1)
end

-- Функция проверки уровня сигнала
function getCSQ()
    -- Отправляем команду несколько раз, пока не получим правильный ответ
    for i = 1, 3 do
        local resp = sendCommand("AT+CSQ", 2)
        -- Проверяем, что это действительно ответ на CSQ
        if resp:find("+CSQ:") then
            local _, _, rssi = resp:find("+CSQ: (%d+),")
            if rssi then
                print("Уровень сигнала:" .. rssi)
                return tonumber(rssi)
            end
        else
   --         print("Попытка " .. i .. ": получен мусор, очищаем буфер...")
            -- Очищаем буфер
            local start = os.clock()
            while os.clock() - start < 0.3 do
                port:read(1)
            end
        end
    end
    print("Не удалось получить CSQ после 3 попыток")
    return nil
end

-- Функция ожидания регистрации в сети
function waitForNetwork(maxWaitTime)
    maxWaitTime = maxWaitTime or 30 -- Ждем до 30 секунд
    print("Ожидание регистрации в сети (до " .. maxWaitTime .. " сек)...")
    local startTime = os.clock()
    while os.clock() - startTime < maxWaitTime do
        local resp = sendCommand("AT+CREG?", 3)
        -- Ищем паттерн +CREG: 0,1 (регистрация в домашней сети)
        -- или +CREG: 0,5 (регистрация в роуминге)
        if resp:find("+CREG: 0,[15]") then
            print("Модем зарегистрирован в сети!")
            -- Узнаем оператора
            local opResp = sendCommand("AT+COPS?", 3)
            local _, _, operator = opResp:find('"+COPS: %d+,%d+,"([^"]+)"')
            if operator then
                print("Оператор: " .. operator)
            end
            return true
        end
        -- Проверяем другие статусы
        if resp:find("+CREG: 0,2") then
            print("  Поиск сети...")
        elseif resp:find("+CREG: 0,3") then
            print("  Регистрация отклонена")
        elseif resp:find("+CREG: 0,4") then
            print("  Неизвестно")
        end
        sleep(2) -- Проверяем каждые 2 секунды
    end
    print("Таймаут ожидания сети")
    return false
end
function getSystemStatus()
    return 1  -- 1 = OK, 0 = Error
end

function relayOn()
    print("  Реле ВКЛ")
    -- Здесь код управления реле
end

function relayOff()
    print("  Реле ВЫКЛ")
    -- Здесь код управления реле
end

function readTemperature()
    return 235  -- 23.5°C * 10
end

function readHumidity()
    return 67   -- 67%
end

function readPressure()
    return 1013 -- 1013 гПа
end

function setTimer(seconds)
    print("  Таймер установлен на " .. seconds .. " сек")
end

function setDelay(seconds)
    print("  Задержка установлена на " .. seconds .. " сек")
end

-- Сохранение звонка
function saveCall(phoneNumber)
    local call = {
        id = #calls + 1,
        phone = phoneNumber,
        date = os.date("%Y-%m-%d"),
        time = os.date("%H:%M:%S"),
        timestamp = os.time()
    }
    table.insert(calls, call)

    local today = os.date("%Y-%m-%d")
    local f = io.open("calls_" .. today .. ".log", "a")
    if f then
        f:write(string.format("[%s] ЗВОНОК с %sn", os.date("%H:%M:%S"), phoneNumber))
        f:close()
    end
    print("  Звонок сохранен и сброшен")
    saveToFile()
    return call
end

-- Сохранение входящих команд
function saveIncomingCommands(phone, raw_text, commands)
    local record = {
        id = #incoming_commands + 1,
        phone = phone,
        raw = raw_text,
        commands = commands,
        date = os.date("%Y-%m-%d"),
        time = os.date("%H:%M:%S"),
        timestamp = os.time()
    }
    table.insert(incoming_commands, record)

    local today = os.date("%Y-%m-%d")
    local f = io.open("commands_" .. today .. ".log", "a")
    if f then
        f:write(string.format("[%s] ОТ: %sn", os.date("%H:%M:%S"), phone))
        f:write("    ТЕКСТ: " .. raw_text .. "n")
        f:write("    КОМАНДЫ:n")
        for _, cmd in ipairs(commands) do
            f:write("      " .. dumpCommand(cmd) .. "n")
        end
        f:write("    " .. string.rep("-", 50) .. "n")
        f:close()
    end

    saveToFile()
    return record
end

-- Сохранение в файл
function saveToFile()
    local f = io.open("modem_data.lua", "w")
    if f then
        f:write("-- Данные модемаnn")

        f:write("calls = {n")
        for _, call in ipairs(calls) do
            f:write(string.format('  {id=%d, phone="%s", date="%s", time="%s", timestamp=%d},n',
                call.id, call.phone, call.date, call.time, call.timestamp))
        end
        f:write("}nn")

        f:write("incoming_commands = {n")
        for _, cmd in ipairs(incoming_commands) do
            f:write(string.format('  {id=%d, phone="%s", raw="%s", date="%s", time="%s", timestamp=%d},n',
                cmd.id, cmd.phone, cmd.raw:gsub('"', '\"'), cmd.date, cmd.time, cmd.timestamp))
        end
        f:write("}nn")

        f:write("outgoing_messages = {n")
        for _, msg in ipairs(outgoing_messages) do
            local safeText = msg.text:gsub('"', '\"')
            f:write(string.format('  {id=%d, phone="%s", text="%s", date="%s", time="%s", timestamp=%d},n',
                msg.id, msg.phone, safeText, msg.date, msg.time, msg.timestamp))
        end
        f:write("}n")

        f:close()
    end
end

-- Загрузка из файла
function loadTables()
    local f = io.open("modem_data.lua", "r")
    if f then
        f:close()
        dofile("modem_data.lua")
        calls = calls or {}
        incoming_commands = incoming_commands or {}
        outgoing_messages = outgoing_messages or {}
        print(string.format("Загружено: звонков %d, команд %d, исх SMS %d",
            #calls, #incoming_commands, #outgoing_messages))
    else
        print("Новый файл данных")
        calls = {}
        incoming_commands = {}
        outgoing_messages = {}
    end
end
-- ИНИЦИАЛИЗАЦИЯ
-- САМОЕ ПРОСТОЕ РЕШЕНИЕ: просто отправляем CSQ несколько раз
function getCSQ_simple()
--    print("Получение уровня сигнала...")
    for i = 1, 3 do
        local resp = sendCommand("AT+CSQ", 2)
        -- Проверяем, содержит ли ответ "+CSQ:"
        if resp and resp:find("+CSQ:") then
            local _, _, rssi = resp:find("+CSQ: (%d+),")
            if rssi then
                print("Уровень сигнала: " .. rssi)
                return tonumber(rssi)
            end
--        else
            -- Если пришел не CSQ, просто логируем и продолжаем
         --   print("Попытка " .. i .. ": получили '" .. resp .. "', пробуем снова...")
        end
        -- Пауза между попытками
        local wait = os.clock()
        while os.clock() - wait < 0.3 do end
    end

    print("Не удалось получить уровень сигнала")
    return nil
end

function isNetworkReady()
    local resp = sendCommand("AT+CREG?", 2)
    -- true если зарегистрирован (0,1 или 0,5)
    return resp:find("0,1") or resp:find("0,5") ~= nil
end

function init()
    if not openPort() then return false end
	port:write("rn")  -- Просто перевод строки для пробуждения
  port:flush()
    -- Ждем 300 мс (используем нашу функцию sleep)
   local wakeStart = os.clock() while os.clock() - wakeStart < 0.3 do end       -- просто ждем
    local s=sendCommand("AT",1);
--	wrcmd("ATE0"); --sendCommand("ATE0",1) --эхо выкл
	if s=="" then print("Модем не отвечает") return false end
    print("Модем отвечает:"..s)
	wrcmd("ATE0"); --sendCommand("ATE0",1) --эхо выкл
	waitForNetwork()
-- Использование
	local rssi = getCSQ_simple()
	if rssi and rssi >= 10 then    print("Сигнал достаточный для работы") end
	wrcmd("AT+IFC=0,0"); sendCommand("AT+IFC=0,0", 1)
    sendCommand("AT+CMGF=1", 1)  -- Текстовый режим для SMS
    sendCommand("AT+CMGD=1,4", 2) --   print("Очистка памяти SIM...")
    sendCommand("AT+CLIP=1", 1) --аон вкл   print("Настройка определителя номера...") — включает отправку номера вызывающего абонента. Теперь после каждого входящего звонка модем будет автоматически присылать строку с номером .
	sendCommand("AT+CMEE=1", 1) --— включает развернутые сообщения об ошибках. Полезно для отладки на случай, если что-то пойдет не так.
    sendCommand("AT+CNMI=2,1", 1)
	sendCommand("AT+CSCLK=2",1) --авто сон
    print("Модем готов к командам с буквенными параметрами")
    return true
end
-- ============================================
-- ГЛАВНЫЙ ЦИКЛ
-- ============================================
function run()
    print("n" .. string.rep("=", 60))
    print("GSM КОНТРОЛЛЕР - КОМАНДЫ С БУКВЕННЫМИ ПАРАМЕТРАМИ")
    print("Формат: КОМАНДА[БУКВА][ЗНАЧЕНИЕ]...")
    print("Примеры:")
    print("  1                    - команда 1 без параметров")
    print("  1t235                - команда 1 с параметром t=235")
    print("  2r1                  - команда 2 с параметром r=1")
    print("  3t235h67             - команда 3 с t=235 и h=67")
    print("  1t235,2r1,3t235h67   - несколько команд")
    print(string.rep("=", 60))

    local lastStats = os.clock()
    local lastPhone = ""
    local lastPhoneTime = 0

    while true do
        local char = port:read(1)
        if char then
            buffer = buffer .. char
            if char == "n" then
                local line = buffer:gsub("r", ""):gsub("n", "")
                buffer = ""
                -- Обработка звонков
                local phone = extractPhoneFromCLIP(line)
                if phone then
                    local currentTime = os.time()
                    if phone == lastPhone and (currentTime - lastPhoneTime) < 21 then
                        -- дубликат
                   else
					port:write("rn") port:flush() -- Просто перевод строки для пробуждения
					-- Ждем 100 мс (используем нашу функцию sleep)
						local wakeStart = os.clock() while os.clock() - wakeStart < 0.1 do end       -- просто ждем
						wrcmd("ATH");
					    print("!!! ВХОДЯЩИЙ ЗВОНОК от " .. phone)
						saveCall(phone)

                        lastPhone = phone
                        lastPhoneTime = currentTime
                    end
                end

                -- Обработка SMS
                local idx = line:match('CMTI: "[^"]+",(%d+)')
                if idx then
                    local num = tonumber(idx)
                    print("!!! ВХОДЯЩЕЕ SMS в ячейке " .. num)

                    sleep(200)
                    local phone, text, date = readSMS(num)
                    if phone and text then
                        print("   От: " .. phone)
                        print("   Текст: " .. text)

                        -- Парсим команды
                        local commands = parseCommands(text)
                        if #commands > 0 then
                            print(string.format("   Найдено команд: %d", #commands))
                            saveIncomingCommands(phone, text, commands)
                            processCommands(phone, commands)
                        else
                            print("   Нет распознанных команд")
                        end

                        deleteSMS(num)
                    end
                end
            end
        else
            if os.clock() - lastStats > 60 then
				local s=sendCommand("AT+CSQ",1); --уровень сигнала
				print("уровень сигнала ",s);
                print(string.format("Звонков: %d, Команд: %d, Отправлено: %d",
                    #calls, #incoming_commands, #outgoing_messages))
                lastStats = os.clock()
            end
            sleep(10)
        end
    end
end
-- ЗАПУСК
loadTables()
if init() then
    print("n" .. string.rep("-", 60))
    print("ТЕКУЩАЯ СТАТИСТИКА")
    print("  Звонков: " .. #calls)
    print("  Команд: " .. #incoming_commands)
    print("  Отправлено SMS: " .. #outgoing_messages)
    print(string.rep("-", 60) .. "n")

    local ok, err = pcall(run)
    if not ok then
        print("Ошибка: " .. tostring(err))
    end
end

if port then port:close() end
print("nПрограмма завершена")
print("Всего звонков: " .. #calls)
print("Всего команд: " .. #incoming_commands)
print("Всего отправлено SMS: " .. #outgoing_messages)
saveToFile()

Поделка 2:

Идея в том, чтобы заменить в Поделке 1 ПК на микроконтроллер ESP32-C3. В результате сделать автономное устройство, которое осуществляет авторизованный доступ в здание путем приема звонков с телефона и идентификацию звонящего по белому списку.

При этом программу решил написать на С.

Спросил мнение DS:

Мой соавтор — DeepSeek - 2
Мой соавтор — DeepSeek - 3

Поделка находится в стадии отладки, поэтому фрагменты кода для примера.

main/gsm.h
#ifndef GSM_H
#define GSM_H

#include <stdint.h>
#include <stdbool.h>

// Настройки SIM800L
#define GSM_UART_PORT          UART_NUM_1
#define GSM_TX_PIN             17
#define GSM_RX_PIN             16
#define GSM_BAUD_RATE          115200
#define GSM_PWR_PIN            4
#define GSM_RST_PIN            5

// Буферы
#define GSM_RX_BUF_SIZE         1024
#define GSM_TX_BUF_SIZE         512
#define GSM_CMD_TIMEOUT_MS      5000
#define GSM_SMS_BUFFER_SIZE     256

// Результаты операций
typedef enum {
    GSM_OK = 0,
    GSM_ERROR,
    GSM_TIMEOUT,
    GSM_NO_SIM,
    GSM_NO_SIGNAL
} gsm_status_t;

// Структура для SMS
typedef struct {
    char phone[20];
    char message[GSM_SMS_BUFFER_SIZE];
    char datetime[32];
    int index;
} sms_data_t;

// Инициализация
void gsm_init(void);
gsm_status_t gsm_power_on(void);
gsm_status_t gsm_check_communication(void);

// SMS функции
gsm_status_t gsm_check_new_sms(int *new_index);
gsm_status_t gsm_read_sms(int index, sms_data_t *sms);
gsm_status_t gsm_delete_sms(int index);
gsm_status_t gsm_send_sms(const char *phone, const char *message);

// Утилиты
bool gsm_is_authorized(const char *phone);
void gsm_parse_command(const char *message, int *cmd, int *params, int *param_count);

#endif // GSM_H
main/gsm.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/uart.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include "gsm.h"
#include "relay.h"
#include "storage.h"

static const char *TAG = "GSM";

// Авторизованные номера (загружаются из NVS)
static char authorized_numbers[5][20] = {
    "+79093436455",  // Ваш номер
    "", "", "", ""
};

// Буфер для ответов
static char response_buffer[1024];

// Отправка AT команды и получение ответа
static gsm_status_t gsm_send_command(const char *cmd, char *response, int timeout_ms) {
    uart_write_bytes(GSM_UART_PORT, cmd, strlen(cmd));
    uart_write_bytes(GSM_UART_PORT, "rn", 2);
    
    int len = 0;
    int total_len = 0;
    TickType_t start_time = xTaskGetTickCount();
    
    memset(response, 0, sizeof(response_buffer));
    
    while ((xTaskGetTickCount() - start_time) < pdMS_TO_TICKS(timeout_ms)) {
        len = uart_read_bytes(GSM_UART_PORT, (uint8_t*)response + total_len, 
                              sizeof(response_buffer) - total_len - 1, pdMS_TO_TICKS(100));
        if (len > 0) {
            total_len += len;
            if (strstr(response, "OKrn") || strstr(response, "ERRORrn")) {
                break;
            }
        }
        vTaskDelay(pdMS_TO_TICKS(10));
    }
    
    return (total_len > 0) ? GSM_OK : GSM_TIMEOUT;
}

// Инициализация UART для GSM
void gsm_init(void) {
    uart_config_t uart_config = {
        .baud_rate = GSM_BAUD_RATE,
        .data_bits = UART_DATA_8_BITS,
        .parity = UART_PARITY_DISABLE,
        .stop_bits = UART_STOP_BITS_1,
        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
        .source_clk = UART_SCLK_APB,
    };
    
    uart_param_config(GSM_UART_PORT, &uart_config);
    uart_set_pin(GSM_UART_PORT, GSM_TX_PIN, GSM_RX_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
    uart_driver_install(GSM_UART_PORT, GSM_RX_BUF_SIZE * 2, GSM_TX_BUF_SIZE * 2, 0, NULL, 0);
    
    ESP_LOGI(TAG, "GSM UART initialized");
}

// Включение питания модема
gsm_status_t gsm_power_on(void) {
    gpio_set_direction(GSM_PWR_PIN, GPIO_MODE_OUTPUT);
    gpio_set_direction(GSM_RST_PIN, GPIO_MODE_OUTPUT);
    
    // Сигнал PWRKEY для включения SIM800L
    gpio_set_level(GSM_PWR_PIN, 1);
    vTaskDelay(pdMS_TO_TICKS(1000));
    gpio_set_level(GSM_PWR_PIN, 0);
    vTaskDelay(pdMS_TO_TICKS(3000));
    
    return GSM_OK;
}

// Проверка связи с модемом
gsm_status_t gsm_check_communication(void) {
    char response[256];
    
    for (int i = 0; i < 3; i++) {
        if (gsm_send_command("AT", response, 2000) == GSM_OK) {
            if (strstr(response, "OK")) {
                ESP_LOGI(TAG, "GSM modem OK");
                return GSM_OK;
            }
        }
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
    
    ESP_LOGE(TAG, "GSM modem not responding");
    return GSM_ERROR;
}

// Проверка нового SMS
gsm_status_t gsm_check_new_sms(int *new_index) {
    char response[256];
    
    *new_index = -1;
    
    if (gsm_send_command("AT+CMGL="REC UNREAD",1", response, 5000) == GSM_OK) {
        // Ищем +CMGL: index,"REC UNREAD"...
        char *p = strstr(response, "+CMGL:");
        if (p) {
            p += 6;
            while (*p == ' ') p++;
            *new_index = atoi(p);
            return GSM_OK;
        }
    }
    
    return GSM_ERROR;
}

// Чтение SMS
gsm_status_t gsm_read_sms(int index, sms_data_t *sms) {
    char cmd[32];
    char response[512];
    
    snprintf(cmd, sizeof(cmd), "AT+CMGR=%d", index);
    
    if (gsm_send_command(cmd, response, 5000) != GSM_OK) {
        return GSM_ERROR;
    }
    
    // Парсинг номера: +CMGR: "REC READ","+79093436455",,"24/03/08,11:32:39+04"
    char *phone_start = strchr(response, '"');
    if (!phone_start) return GSM_ERROR;
    phone_start = strchr(phone_start + 1, '"');
    if (!phone_start) return GSM_ERROR;
    phone_start++;
    
    char *phone_end = strchr(phone_start, '"');
    if (!phone_end) return GSM_ERROR;
    
    int len = phone_end - phone_start;
    if (len > 0 && len < (int)sizeof(sms->phone) - 1) {
        strncpy(sms->phone, phone_start, len);
        sms->phone[len] = '';
    }
    
    // Ищем текст сообщения (после последней кавычки)
    char *text_start = strrchr(response, '"');
    if (text_start) {
        text_start = strchr(text_start + 1, 'n');
        if (text_start) {
            text_start++;
            char *text_end = strstr(text_start, "OK");
            if (text_end) {
                len = text_end - text_start;
                if (len > 0 && len < (int)sizeof(sms->message) - 1) {
                    strncpy(sms->message, text_start, len);
                    sms->message[len] = '';
                    // Удаляем r
                    char *p;
                    while ((p = strchr(sms->message, 'r')) != NULL) {
                        *p = ' ';
                    }
                }
            }
        }
    }
    
    sms->index = index;
    return GSM_OK;
}

// Удаление SMS
gsm_status_t gsm_delete_sms(int index) {
    char cmd[32];
    char response[32];
    
    snprintf(cmd, sizeof(cmd), "AT+CMGD=%d", index);
    return gsm_send_command(cmd, response, 2000);
}

// Отправка SMS
gsm_status_t gsm_send_sms(const char *phone, const char *message) {
    char cmd[64];
    char response[64];
    
    // Текстовый режим
    gsm_send_command("AT+CMGF=1", response, 1000);
    vTaskDelay(pdMS_TO_TICKS(100));
    
    // Подготовка к отправке
    snprintf(cmd, sizeof(cmd), "AT+CMGS="%s"", phone);
    if (gsm_send_command(cmd, response, 5000) != GSM_OK) {
        return GSM_ERROR;
    }
    
    // Отправка текста и Ctrl+Z
    if (strstr(response, ">")) {
        uart_write_bytes(GSM_UART_PORT, message, strlen(message));
        uart_write_bytes(GSM_UART_PORT, "x1A", 1);  // Ctrl+Z
        
        vTaskDelay(pdMS_TO_TICKS(5000));
        
        // Проверка ответа
        memset(response, 0, sizeof(response));
        int len = uart_read_bytes(GSM_UART_PORT, (uint8_t*)response, sizeof(response) - 1, pdMS_TO_TICKS(1000));
        if (len > 0 && strstr(response, "OK")) {
            ESP_LOGI(TAG, "SMS sent to %s", phone);
            return GSM_OK;
        }
    }
    
    return GSM_ERROR;
}

// Проверка авторизации
bool gsm_is_authorized(const char *phone) {
    for (int i = 0; i < 5; i++) {
        if (authorized_numbers[i][0] == '') continue;
        
        // Сравниваем номера
        if (strcmp(phone, authorized_numbers[i]) == 0) {
            return true;
        }
        
        // Проверка формата 8 вместо +7
        if (phone[0] == '8' && authorized_numbers[i][0] == '+') {
            char phone_with_7[20];
            snprintf(phone_with_7, sizeof(phone_with_7), "+7%s", phone + 1);
            if (strcmp(phone_with_7, authorized_numbers[i]) == 0) {
                return true;
            }
        }
    }
    return false;
}

// Парсинг команды из SMS (формат: "команда параметр")
void gsm_parse_command(const char *message, int *cmd, int *params, int *param_count) {
    *cmd = 0;
    *param_count = 0;
    
    // Парсим первую цифру - команда
    char *endptr;
    *cmd = strtol(message, &endptr, 10);
    
    // Если есть параметры
    if (endptr && *endptr != '') {
        char *p = (char*)endptr;
        while (*p != '' && *param_count < 5) {
            // Пропускаем не-цифры
            while (*p != '' && (*p < '0' || *p > '9')) p++;
            if (*p == '') break;
            
            // Читаем параметр
            params[*param_count] = strtol(p, &p, 10);
            (*param_count)++;
        }
    }
}
main/main.c
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "gsm.h"
#include "relay.h"
#include "wifi_ap.h"
#include "web_server.h"
#include "storage.h"

static const char *TAG = "MAIN";

// Задача обработки GSM событий
void gsm_task(void *pvParameters) {
    int last_sms_index = -1;
    int check_count = 0;
    
    while (1) {
        // Проверка новых SMS каждые 2 секунды
        vTaskDelay(pdMS_TO_TICKS(2000));
        
        int new_index;
        if (gsm_check_new_sms(&new_index) == GSM_OK && new_index != last_sms_index) {
            if (new_index >= 0) {
                ESP_LOGI(TAG, "New SMS at index %d", new_index);
                
                sms_data_t sms;
                if (gsm_read_sms(new_index, &sms) == GSM_OK) {
                    ESP_LOGI(TAG, "From: %s", sms.phone);
                    ESP_LOGI(TAG, "Msg: %s", sms.message);
                    
                    // Проверка авторизации
                    if (gsm_is_authorized(sms.phone)) {
                        ESP_LOGI(TAG, "Authorized number, processing command");
                        
                        // Парсинг команды
                        int cmd, params[5] = {0}, param_count = 0;
                        gsm_parse_command(sms.message, &cmd, params, &param_count);
                        
                        // Обработка команд
                        if (cmd == 1) {
                            // STATUS
                            char status[128];
                            snprintf(status, sizeof(status), 
                                "Relay status: 1=%s 2=%s 3=%s 4=%s",
                                relay_get_state(0) ? "ON" : "OFF",
                                relay_get_state(1) ? "ON" : "OFF",
                                relay_get_state(2) ? "ON" : "OFF",
                                relay_get_state(3) ? "ON" : "OFF");
                            gsm_send_sms(sms.phone, status);
                            
                        } else if (cmd >= 11 && cmd <= 14) {
                            // RELAY control (11=relay1, 12=relay2, etc)
                            int relay = cmd - 11;
                            if (param_count > 0) {
                                relay_set(relay, params[0] == 1);
                                char response[32];
                                snprintf(response, sizeof(response), "Relay%d %s", 
                                    relay + 1, params[0] == 1 ? "ON" : "OFF");
                                gsm_send_sms(sms.phone, response);
                            }
                            
                        } else if (cmd == 2) {
                            // ALL ON/OFF
                            if (param_count > 0) {
                                relay_all_set(params[0] == 1);
                                gsm_send_sms(sms.phone, params[0] == 1 ? "All ON" : "All OFF");
                            }
                            
                        } else if (cmd == 3) {
                            // WiFi mode switch
                            if (wifi_get_mode() == WIFI_MODE_AP) {
                                wifi_set_mode(WIFI_MODE_STA);
                                gsm_send_sms(sms.phone, "WiFi mode: Client");
                            } else {
                                wifi_set_mode(WIFI_MODE_AP);
                                gsm_send_sms(sms.phone, "WiFi mode: AP");
                            }
                            wifi_ap_stop();
                            vTaskDelay(pdMS_TO_TICKS(1000));
                            wifi_ap_start();
                            
                        } else {
                            gsm_send_sms(sms.phone, "Unknown command");
                        }
                        
                    } else {
                        ESP_LOGW(TAG, "Unauthorized number: %s", sms.phone);
                        gsm_send_sms(sms.phone, "ERROR: Unauthorized");
                    }
                    
                    // Удаляем прочитанное SMS
                    gsm_delete_sms(new_index);
                }
                
                last_sms_index = new_index;
            }
        }
        
        // Периодическая проверка сигнала
        check_count++;
        if (check_count >= 30) {  // ~1 минута
            check_count = 0;
            ESP_LOGI(TAG, "System running: Relays %d%d%d%d", 
                relay_get_state(0), relay_get_state(1),
                relay_get_state(2), relay_get_state(3));
        }
    }
}

void app_main(void) {
    ESP_LOGI(TAG, "GSM Relay Controller starting...");
    
    // Инициализация хранилища
    storage_init();
    
    // Инициализация реле
    relay_init();
    
    // Инициализация GSM
    gsm_init();
    
    // Включение модема
    if (gsm_power_on() != GSM_OK) {
        ESP_LOGE(TAG, "Failed to power on GSM");
    }
    
    // Проверка связи
    if (gsm_check_communication() != GSM_OK) {
        ESP_LOGE(TAG, "GSM communication failed");
    }
    
    // Настройка WiFi AP
    wifi_ap_init();
    wifi_set_mode(WIFI_MODE_AP);
    wifi_ap_start();
    
    // Запуск веб-сервера
    web_server_start();
    
    // Создание задачи обработки GSM
    xTaskCreate(gsm_task, "gsm_task", 4096, NULL, 5, NULL);
    
    ESP_LOGI(TAG, "System ready");
}

Поделка 3:

Так как использую программу QUIK для торговли на фондовом рынке, то решил автоматизировать двухфакторную аутентификацию.

Для тех, кто не знает, что это такое, поясняю.

При запуске торгового терминала QUIK, необходимо сначала ввести пароль в соответствующее окно. После этого программа подключится к серверу брокера и запросит у вас код , который получите на телефон(смартфон) от брокера. Необходимо этот код вручную ввести в окно программы.

Так как у меня есть модем, который я сделал в поделки 1, то почему бы не использовать его для автоматизации старта QUIK.

Решил написать скрипт на AutoIt.

Предварительно узнать у DS о возможных вариантах решения.

Мой соавтор — DeepSeek - 4

Ответ DS:

Мой соавтор — DeepSeek - 5

Поделка фактически состоит из двух задач.

Первая задача связана с запуском QUIK и автоматическим вводам пароля и кода в соответствующие окна. При ее решении пароль и код разместил в файлах. Так как задача простая, то на ее решение ушло примерно 20 минут.

Результирующий скрипт:
; =======================================================================
; Скрипт автоматического ввода пароля и кода для QUIK
; автор nikolz & DS
; Версия: 3.0 (с поддержкой параметров и настроек)
; =======================================================================

; --- Настройки ---
$sQuikPath = "C:QUIKinfo.exe"
$sQuikParams = ""  ; Сюда добавьте параметры из ярлыка, если есть
$sQuikWorkingDir = "C:QUIK"  ; Рабочая папка (обычно там же, где info.exe)

$sPasswordFile = @ScriptDir & "password.txt"
$sCodeFile = @ScriptDir & "code.txt"

; --- Чтение пароля и кода ---
Local $sPassword = FileReadLine($sPasswordFile)
If @error Then
    MsgBox(16, "Ошибка", "Не удалось прочитать файл пароля")
    Exit
EndIf

Local $sCode = FileReadLine($sCodeFile)
If @error Then
    MsgBox(16, "Ошибка", "Не удалось прочитать файл с кодом")
    Exit
EndIf

; --- Запуск QUIK с рабочей папкой ---
; Это ключевой момент! QUIK должен быть запущен из правильной директории
; чтобы увидеть все сохраненные настройки
Run('"' & $sQuikPath & '" ' & $sQuikParams, $sQuikWorkingDir)

; --- Ждем появления окна пароля (с таймаутом) ---
; Окно может появиться не сразу, т.к. сначала грузятся настройки
Local $hFirstWindow = 0
Local $iMaxWait = 30  ; ждем до 30 секунд

For $i = 1 To $iMaxWait
    $hFirstWindow = WinGetHandle("[CLASS:#32770]")
    If $hFirstWindow Then
        Local $sTitle = WinGetTitle($hFirstWindow)
        If StringInStr($sTitle, "пароль") Or StringInStr($sTitle, "Идентификация") Then
            ExitLoop
        EndIf
    EndIf
    Sleep(1000)
Next

If Not $hFirstWindow Then
    MsgBox(16, "Ошибка", "Окно пароля не появилось за " & $iMaxWait & " секунд")
    Exit
EndIf

; --- Ввод пароля ---
WinActivate($hFirstWindow)
Sleep(500)
ControlSend($hFirstWindow, "", "[CLASS:Edit; INSTANCE:2]", $sPassword)
Sleep(300)
ControlSend($hFirstWindow, "", "", "{ENTER}")

; --- Ждем окно для кода ---
Sleep(2000)  ; Даем время на обработку пароля

Local $hSecondWindow = WinWait("[CLASS:QuikGUI]", "", 20)
If Not $hSecondWindow Then
    ; Если не нашли по классу, ищем по заголовку
    $hSecondWindow = WinWait("Введите код", "", 10)
EndIf

If $hSecondWindow Then
    WinActivate($hSecondWindow)
    Sleep(500)
    ControlSend($hSecondWindow, "", "[CLASS:Edit; INSTANCE:1]", $sCode)
    Sleep(300)
    ControlSend($hSecondWindow, "", "", "{ENTER}")
    MsgBox(64, "Успех", "Авторизация выполнена!")
Else
    MsgBox(48, "Предупреждение", "Окно кода не найдено, но пароль введен")
EndIf

Exit

Вторая задача – прием кода из SMS от брокера

Решение этой задачи находится в стадии отладки, поэтому скрипт лишь для примера:

AutoIt-скрипт для работы с модемом
; =======================================================================
; Модуль получения кода с GSM-модема (AutoIt версия)
; Версия: 1.0
; Основан на ATCmd.au3 UDF
; =======================================================================

#include <ATCmd.au3>
#include <FileConstants.au3>
#include <MsgBoxConstants.au3>
#include <StringConstants.au3>

; --- Параметры командной строки ---
; Формат: script.exe "ComPort=COM3;BaudRate=9600;SenderNumber=+79...;Timeout=60" "output.txt"

If $CmdLine[0] < 2 Then
    ConsoleWrite("Использование: " & @ScriptName & ' "параметры" "выходной_файл"' & @CRLF)
    Exit 1
EndIf

Local $sSettings = $CmdLine[1]
Local $sOutputFile = $CmdLine[2]
Local $sLogFile = @ScriptDir & "modem_autoit.log"

; --- Очищаем лог при запуске ---
FileDelete($sLogFile)
LogWrite("=== Запуск AutoIt-модуля модема ===")

; --- Парсинг параметров ---
Local $aSettings = StringSplit($sSettings, ";")
Local $sComPort = "COM3"
Local $iBaudRate = 9600
Local $sSenderNumber = ""
Local $iTimeout = 60
Local $sSimPIN = ""

For $i = 1 To $aSettings[0]
    Local $aPair = StringSplit($aSettings[$i], "=")
    If $aPair[0] >= 2 Then
        Switch $aPair[1]
            Case "ComPort"
                $sComPort = $aPair[2]
            Case "BaudRate"
                $iBaudRate = Number($aPair[2])
            Case "SenderNumber"
                $sSenderNumber = $aPair[2]
            Case "Timeout"
                $iTimeout = Number($aPair[2])
            Case "SimPIN"
                $sSimPIN = $aPair[2]
        EndSwitch
    EndIf
Next

LogWrite("Порт: " & $sComPort)
LogWrite("Скорость: " & $iBaudRate)
LogWrite("Номер отправителя: " & $sSenderNumber)
LogWrite("Таймаут: " & $iTimeout & " сек")

; --- Главная функция получения кода ---
Local $sCode = GetCodeFromModem()
Local $iResult = 1  ; 0 - успех, 1 - ошибка

If $sCode <> "" Then
    ; Сохраняем код в выходной файл
    Local $hFile = FileOpen($sOutputFile, $FO_OVERWRITE)
    If $hFile <> -1 Then
        FileWrite($hFile, $sCode)
        FileClose($hFile)
        LogWrite("Код сохранен в файл: " & $sCode)
        $iResult = 0
    Else
        LogWrite("ОШИБКА: Не удалось создать выходной файл")
    EndIf
Else
    LogWrite("ОШИБКА: Код не получен")
EndIf

LogWrite("=== Завершение работы ===")
Exit $iResult

; ==============================================================================
; ФУНКЦИИ
; ==============================================================================

; #FUNCTION# ===================================================================
; Name ..........: GetCodeFromModem
; Description ...: Получает код подтверждения из SMS с модема
; Syntax ........: GetCodeFromModem()
; Return values .: Успех - строка с кодом, Неудача - пустая строка
; ==============================================================================
Func GetCodeFromModem()
    Local $sCode = ""
    Local $hModem = 0
    Local $iMaxAttempts = Int($iTimeout / 2)  ; Проверяем каждые 2 секунды
    
    LogWrite("Инициализация модема на порту " & $sComPort)
    
    ; Открываем соединение с модемом
    $hModem = _ATCmd_Open($sComPort, $iBaudRate)
    If @error Then
        LogWrite("ОШИБКА: Не удалось открыть COM-порт " & $sComPort)
        Return ""
    EndIf
    LogWrite("Соединение с модемом установлено")
    
    ; Проверяем наличие SIM-карты
    If Not _ATCmd_IsSIMInserted($hModem) Then
        LogWrite("ОШИБКА: SIM-карта не обнаружена")
        _ATCmd_Close($hModem)
        Return ""
    EndIf
    LogWrite("SIM-карта обнаружена")
    
    ; Проверяем, требуется ли ввод PIN-кода
    If _ATCmd_IsPINRequired($hModem) Then
        LogWrite("Требуется PIN-код SIM-карты")
        If $sSimPIN = "" Then
            LogWrite("ОШИБКА: PIN не указан в настройках")
            _ATCmd_Close($hModem)
            Return ""
        EndIf
        _ATCmd_SetPIN($hModem, $sSimPIN)
        Sleep(2000)
        LogWrite("PIN введен")
    EndIf
    
    ; Проверяем регистрацию в сети
    If Not _ATCmd_WaitForNetwork($hModem, 30) Then
        LogWrite("ОШИБКА: Модем не зарегистрирован в сети")
        _ATCmd_Close($hModem)
        Return ""
    EndIf
    LogWrite("Модем зарегистрирован в сети")
    
    ; Получаем уровень сигнала для информации
    Local $iCSQ = _ATCmd_GetCSQ($hModem)
    If $iCSQ <> -1 Then
        LogWrite("Уровень сигнала: " & $iCSQ)
    EndIf
    
    ; Настраиваем текстовый режим для SMS
    _ATCmd_SMS_SetTextMode($hModem)
    
    ; --- Цикл ожидания SMS ---
    LogWrite("Ожидание SMS с кодом от " & $sSenderNumber & "...")
    
    For $i = 1 To $iMaxAttempts
        Sleep(2000)
        LogWrite("Проверка SMS (попытка " & $i & " из " & $iMaxAttempts & ")")
        
        ; Получаем список всех SMS
        Local $aSMSList = _ATCmd_SMS_ListMessages($hModem, "ALL")
        
        If Not @error And IsArray($aSMSList) Then
            For $j = 0 To UBound($aSMSList) - 1
                ; Формат: [0] - индекс, [1] - статус, [2] - отправитель, [3] - дата, [4] - текст
                Local $sSender = $aSMSList[$j][2]
                Local $sText = $aSMSList[$j][4]
                Local $sIndex = $aSMSList[$j][0]
                
                LogWrite("  Найдено SMS от: " & $sSender)
                
                ; Проверяем отправителя (ищем номер брокера)
                If StringInStr($sSender, $sSenderNumber) Then
                    LogWrite("  Совпадение отправителя!")
                    
                    $sCode = ExtractCodeFromText($sText)
                    
                    If $sCode <> "" Then
                        LogWrite("  Код найден: " & $sCode)
                        
                        ; Удаляем прочитанное SMS
                        _ATCmd_SMS_DeleteMessage($hModem, $sIndex)
                        LogWrite("  SMS удалено")
                        
                        ExitLoop 2
                    Else
                        LogWrite("  Внимание: код не найден в тексте: " & $sText)
                    EndIf
                EndIf
            Next
        Else
            LogWrite("  Новых SMS не найдено")
        EndIf
    Next
    
    _ATCmd_Close($hModem)
    LogWrite("Соединение с модемом закрыто")
    
    Return $sCode
EndFunc

; #FUNCTION# ===================================================================
; Name ..........: ExtractCodeFromText
; Description ...: Извлекает цифровой код из текста SMS
; Syntax ........: ExtractCodeFromText($sText)
; Parameters ....: $sText - текст SMS
; Return values .: строка с кодом или пустая строка
; ==============================================================================
Func ExtractCodeFromText($sText)
    ; Ищем последовательность из 4-8 цифр
    Local $aMatches = StringRegExp($sText, "(d{4,8})", $STR_REGEXPARRAYMATCH)
    
    If Not @error And UBound($aMatches) > 0 Then
        Local $sCode = $aMatches[0]
        ; Дополнительная проверка: код обычно не начинается с 0
        If StringLeft($sCode, 1) <> "0" Then
            Return $sCode
        EndIf
    EndIf
    
    ; Альтернативный поиск - если код в формате "код: 123456" или "code: 123456"
    $aMatches = StringRegExp($sText, "(?i)(?:код|code|pass|пароль)[:s]*(d{4,8})", $STR_REGEXPARRAYMATCH)
    If Not @error And UBound($aMatches) > 0 Then
        Return $aMatches[0]
    EndIf
    
    Return ""
EndFunc

; #FUNCTION# ===================================================================
; Name ..........: LogWrite
; Description ...: Записывает сообщение в лог-файл
; ==============================================================================
Func LogWrite($sMessage)
    FileWriteLine($sLogFile, @HOUR & ":" & @MIN & ":" & @SEC & " - " & $sMessage)
EndFunc

Автор: nikolz

Источник

Rambler's Top100