Исправить имеющийся скетч для wifi-шлюза (опроса modbus rtu устройств)

Тема в разделе "Закажу проект", создана пользователем Kotopes, 27 янв 2020.

  1. Kotopes

    Kotopes Нерд

    Необходимо исправить ошибки в скрипте.

    Имеется WiFi-шлюз MODBUS TCP-RTU на esp8266 https://mjdm.ru/forum/viewtopic.php?f=8&t=3363, под этот шлюз есть скрипт, но при компиляции прошивки выскакивает ошибка.

    > dofile("ce102m_gate.lua");
    ce102m_gate.lua:166: attempt to call field 'alarm' (a nil value)
    stack traceback:
    ce102m_gate.lua:166: in main chunk
    [C]: in function 'dofile'
    stdin:1: in main chunk

    Если использовать версию прошивки 1.5.4.1-final (frozen, for 512KB flash), то выложенный тут скетч можно не править все заливается сразу и без проблем.

    НО MQTT не работает!!!

    ошибка PANIC: unprotected error in call to Lua API (ce102m_gate.lua:186: attempt to index global 'mqtt' (a nil value))

    Во вложение прошивка и скетч

    Код (C++):
    1 WIFI_SSID = ""
    2 WIFI_PASS = ""
    3 -- Параметры MQTT
    4 MQTT_BrokerIP = "192.168.1.11"
    5 MQTT_BrokerPort = 1883
    6 MQTT_ClientID = "esp-energomera"
    7 MQTT_Client_user = "user"
    8 MQTT_Client_password = "password"
    9 MQTT_TopicString = "/CE102M/"
    10 -- Параметры RS485
    11 RS485_BaudRate = 9600
    12 RS485_DataBits = 7
    13 RS485_ParityBits = uart.PARITY_EVEN
    14 RS485_StopBits = uart.STOPBITS_1
    15 RS485_TxOn_Pin = 6
    16 -- Параметры опроса счётчика
    17 CE102M_Addr = "" -- адрес счётчика ("" или nil для безадресного обмена)
    18 CE102M_CyclePeriod = 10000
    19 CE102M_RequestPeriod = 1000
    20 -- Переменные
    21 local wifi_status_old = 0
    22 local rx_buff = ""
    23 local last_param_name
    24 local MQTT_connected = false
    25 local m
    26 -- Запрашиваемые из счётчика параметры
    27 local ce102m_params = {"VOLTA","CURRE","POWEP","COS_f","FREQU"}
    28 local ce102m_step
    29
    30
    31 -- Настраиваем альтернативные ножки UART (GPIO13, GPIO15)
    32 gpio.mode(4, gpio.INPUT, gpio.PULLUP)
    33 gpio.mode(8, gpio.OUTPUT)
    34 gpio.write(8, 1)
    35 -- Настраиваем ножку управления трансивером RS485
    36 gpio.mode(RS485_TxOn_Pin, gpio.OUTPUT)
    37 gpio.write(RS485_TxOn_Pin, 0)
    38
    39 -- Считаем период передачи 1 байта данных через RS485
    40 --- Биты данных и старт-бит
    41 Bits = RS485_DataBits + 1
    42 --- Бит чётности
    43 if (RS485_ParityBits ~= uart.PARITY_NONE) then Bits = Bits + 1 end
    44 --- Стоп-бит
    45 if (RS485_StopBits == uart.STOPBITS_1) then Bits = Bits + 1
    46 elseif (RS485_StopBits == uart.STOPBITS_1_5) then Bits = Bits + 1.5
    47 else Bits = Bits + 2 end
    48 ByteTransmitTimeUs = RS485_BaudRate / Bits
    49 ByteTransmitTimeUs = 1000000 / ByteTransmitTimeUs
    50
    51 --  Стартуем подключение к wifi-точке
    52 wifi.setmode(wifi.STATION)
    53 wifi.sta.config(WIFI_SSID, WIFI_PASS)
    54 wifi.sta.connect()
    55
    56 -- Функция отправки сообщения по MQTT
    57 function MQTT_send(topic, data)
    58    if (MQTT_connected) and (m ~= nil) then
    59        m:publish(topic, data, 0, 0, nil)
    60    end
    61 end
    62
    63 -- Функция считает BCC (простая сумма всех байт с переполнением) по строке aData
    64 function process_bcc(aData)
    65    local bcc = 0x00
    66    for i = 1, #aData do
    67        bcc = bit.band(0x7F, bcc + string.byte(aData, i))
    68    end
    69    return bcc
    70 end
    71
    72 -- Функция-обработчик входящего пакета
    73 function uart_paket_rx()
    74    -- Информационное сообщение?
    75    if (string.byte(rx_buff, 1) ~= 0x02) then return end
    76    -- Контрольная сумма сошлась?
    77    local bcc = process_bcc(string.sub(rx_buff, 2, #rx_buff - 1))
    78    if (string.byte(rx_buff, #rx_buff) ~= bcc) then return end
    79    -- Ищем имя параметра в пакете
    80    local a = 0
    81    for rx_buff_one in string.gmatch(rx_buff, last_param_name.."%([0-9]+%.[0-9]+%)") do
    82        a = a + 1    --увеличиваем счетчик
    83
    84        local str = last_param_name.."%("
    85        local pos1 = string.find(rx_buff_one, str)
    86        local pos2 = string.find(rx_buff_one, "%)")
    87        if (pos1 == nil) and (pos2 == nil) then return end
    88
    89        -- Извлекаем значение и отправляем по MQTT
    90        pos1 = pos1 + #str - 1
    91        str = string.sub(rx_buff_one, pos1, pos2 - 1)
    92        --print(last_param_name.." "..a.." "..str)
    93        --print(a)
    94        if (str ~= "0.00") then
    94            MQTT_send(MQTT_TopicString..last_param_name..a, str)
    96        end
    97    end
    98 end
    99
    100 -- Функция настраивает UART
    101 function uart_setup()
    102    node.output(function(str) end, 0)
    103    uart.alt(1)
    104    uart.setup(0, RS485_BaudRate, RS485_DataBits, RS485_ParityBits, RS485_StopBits, 0)
    105
    106    uart.on("data", 1, function(data)
    107        rx_buff = rx_buff..data
    108        if (#rx_buff > 256) then rx_buff = "" end
    109
    110        -- [Пере]запускаем таймер ожидания очередного байта
    111        tmr.alarm(6, 5, tmr.ALARM_SINGLE, function()
    112            uart_paket_rx()
    113        end)
    114    end, 0)
    115 end
    116
    117 -- Функция отправляет пакет в RS485
    118 function RS485_send(paket)
    119    gpio.write(RS485_TxOn_Pin, 1)
    120    uart.write(0, paket)
    121    tmr.delay(ByteTransmitTimeUs * #paket)
    122    gpio.write(RS485_TxOn_Pin, 0)
    123    rx_buff = ""
    124 end
    125
    126 -- Функция отправляет в счётчик запрос на соединение (адресный или безадресный)
    127 function ce102m_start(addr)
    128    if (addr == nil) then addr = "" end
    129    RS485_send(string.char(0x2F,0x3F)..addr..string.char(0x21,0x0D,0x0A))
    130 end
    131
    132 -- Функция выдаёт ASK, счётчик отвечает своим номером
    133 function ce102m_p0()
    134    RS485_send(string.char(0x06,0x30,0x35,0x31,0x0D,0x0A))
    135 end
    136
    137  -- Функция запрашивает у счётчика параметр по мнемонике
    138  function ce102m_get_param(param_name)
    139     local request = string.char(0x01,0x52,0x31,0x02)..param_name.."()"..string.char(0x03)
    140     local bcc = process_bcc(string.sub(request, 2, #request))
    141     request = request..string.char(bcc)
    142     RS485_send(request)
    143     last_param_name = param_name
    144 end
    145
    146 -- Функция отправляет в счётчик запрос на разъединение
    147 function ce102m_stop()
    148     RS485_send(string.char(0x01,0x42,0x30,0x03,0x75))
    149 end
    150
    151 -- Функция отправляет в счётчик очередной запрос
    152 function NextRequest()
    153     if (ce102m_step == 1) then ce102m_start(CE102M_Addr)
    154     elseif (ce102m_step == 2) then ce102m_p0()
    155     elseif (ce102m_step == (#ce102m_params + 3)) then ce102m_stop()
    156     else ce102m_get_param(ce102m_params[ce102m_step - 2])
    157     end
    158 end
    159
    160 -- Таймеры опроса счётчика
    161 tmr.alarm(1, CE102M_CyclePeriod, tmr.ALARM_AUTO, function()
    162    ce102m_step = 1
    163
    164     tmr.alarm(2, CE102M_RequestPeriod, tmr.ALARM_AUTO, function()
    165        NextRequest(ce102m_step)
    166        ce102m_step = ce102m_step + 1
    167        if (ce102m_step > (#ce102m_params + 3)) then
    168             tmr.unregister(2)
    169         end
    170     end)
    171 end)
    172
    173 -- Таймер установки соединения по WiFi и MQTT
    174 tmr.alarm(0, 5000, tmr.ALARM_AUTO, function()
    175     print("tmr0 "..wifi_status_old.." "..wifi.sta.status())
    176
    177     if wifi.sta.status() == 5 then -- подключение есть
    178         if wifi_status_old ~= 5 then -- Произошло подключение к Wifi, IP получен
    179             print(wifi.sta.getip())
    180        
    181            m = mqtt.Client(MQTT_ClientID, 120, MQTT_Client_user, MQTT_Client_password)
    182
    183            -- Определяем обработчики событий от клиента MQTT
    184            m:on("connect", function(client)
    185                -- Мы подключились к брокеру
    186                MQTT_connected = true
    187                uart_setup()
    188            end)
    189            m:on("offline", function(client)
    190                -- Мы отключились от брокера
    191                MQTT_connected = false
    192            end)
    193
    194            m:connect(MQTT_BrokerIP, MQTT_BrokerPort, 0, 1, nil)
    195        else
    196            -- WiFi-подключение есть и не разрывалось
    197        end
    198    else
    199        wifi.sta.connect()
    200    end
    201
    202    -- Запоминаем состояние подключения к Wifi для следующего такта таймера
    203    wifi_status_old = wifi.sta.status()
    204 end)
     
     

    Вложения:

    • ce102m_gate.zip
      Размер файла:
      257 КБ
      Просмотров:
      313
    Последнее редактирование: 29 янв 2020
  2. ИгорьК

    ИгорьК Гуру

    Может этот сгодится?
     
  3. alp69

    alp69 Форумчанин

    В 166 строке ошибка. Пока не оформите код как предписывает закрепленное в шапке форума сообщение, никто ковыряться в вашем коде не будет. Читайте.
    Я навскидку увидел это (выделил жирным зачеркнутым):
    Плюс отсутствует закрывающая скобка для tmr.alarm. Но какая это строка - даже не собираюсь считать.
     
  4. Kotopes

    Kotopes Нерд

    Это рядом, но немного не то что я хочу, у меня счетчик энергомера и мне надо с него снять показания через rs485
     
    Последнее редактирование: 27 янв 2020
  5. alp69

    alp69 Форумчанин

    Ошибка в 186 строке. Надеюсь, в гугл-переводчике не забанили?
     
  6. Kotopes

    Kotopes Нерд

    Не знал что так можно...
     
  7. Kotopes

    Kotopes Нерд

    хм...поправить возможно?
     
  8. alp69

    alp69 Форумчанин

    А теперь в режиме редактирования в первом сообщении выделите весь код и нажмите кнопку нумерованного списка. Тем самым пронумеруете строки.
    Возможно. Поправляйте.
     
  9. alp69

    alp69 Форумчанин

    Но лучше код с пронумерованными строками вставить заново. А то, благодаря вставке его простым текстом, движок форума автозаменой вставил смайлики. Которые после после форматирования по правилам стали опять символами:
    ...и такого по листингу дофига!
     
    Последнее редактирование: 27 янв 2020
  10. alp69

    alp69 Форумчанин

    Проверяйте все функции, в которых присутствуют переменные, объявленные за пределами этих функций как локальные. Своими словами лень писать. Вот вам цитата:
    Гуглите "LUA область видимости переменной". Плюс проверяйте, присвоено ли нужное значение локальной переменной перед ее употреблением.
     
  11. alp69

    alp69 Форумчанин

    И разберитесь с тем, что происходит с переменной "m". Она не инициирована. То есть nil. А далее вы пытаетесь этим оперировать, причем используете ее в блоке if при отправке сообщения. Но так как она равна nil, нихрена не происходит.
     
  12. ИгорьК

    ИгорьК Гуру

    upload_2020-1-27_22-55-10.png
     
  13. Kotopes

    Kotopes Нерд

    если бы я смог в этом разобраться, цены бы мне не было)
     
  14. ИгорьК

    ИгорьК Гуру

    Завтра будет время и компьютер - посмотрю.
     
  15. Kotopes

    Kotopes Нерд

  16. ИгорьК

    ИгорьК Гуру

    А вы там при компиляции галочку на mqtt ставили?
     
  17. ИгорьК

    ИгорьК Гуру

    Кстати, а этот электпосчетчик, что в статье по ссылке - он сертифицирован в России? Я хочу уже некоторое время поменять свой, но как-то не знаю на что.
     
  18. Kotopes

    Kotopes Нерд

    я наткнулся на эту статью и поставил счетчик Энергомеру... на свою голову... уже года 2 не могу с него содрать показания...лучше бы Меркурий поставил там намного проще шлюз сделать...

    Счетчики SDM220 сертификацию не имеют
     
  19. Kotopes

    Kotopes Нерд

    да конечно ставил
     
  20. ИгорьК

    ИгорьК Гуру

    И этот скрипт для него?