Скрыть объявление
На нашем форуме недоступен просмотр изображений для неавторизованных пользователей. Если Вы уже зарегистрированы на нашем форуме, то можете войти. Если у Вас еще нет аккаунта, мы будем рады, если Вы к нам присоединитесь. Зарегистрироваться Вы можете здесь.

Нужна помощь Ошибки выделения памяти

Тема в разделе "MicroPython", создана пользователем arrowcircle, 26 окт 2018.

  1. arrowcircle

    arrowcircle Новичок

    Сообщения:
    23
    Симпатии:
    1
    Может быть кто-то сталкивался с проблемой выделения памяти?
    Делаю простой скрипт с bme280 и mqtt.
    BME280.py залит на плату и подключается из фс. MQTT подключается из стандартной библиотеки.

    main.py
    Код (Text):
    1.  
    2. import gc
    3. from machine import Pin
    4. from machine import I2C
    5. import time
    6. import BME280
    7. from umqtt.simple import MQTTClient
    8.  
    9. i2c = I2C(scl=Pin(4),sda=Pin(5), freq=10000)
    10. bme = BME280.BME280(i2c=i2c)
    11. c = MQTTClient("umqtt_client", "mqtt.server")
    12. c.connect()
    13.  
    14. while True:
    15.   t, p, h = bme.temperature, bme.pressure * 1000, bme.humidity
    16.   c.publish(b"foo_topic", res)
    17.   c.check_msg()
    18.   time.sleep(1)
    19.  
    20. c.disconnect()
    21.  
    Ошибка на строке t, p, h = ....
    Код (Text):
    1.  
    2. MemoryError: memory allocation failed, allocating 9001 bytes
    3.  
    Последняя версия микропитона, esp8266 nodemcu v3.
    Кто-то сталкивался с такой проблемой? В интернете пишут, что это связано с фрагментацией хипа, но как с этим бороться - непонятно.
    Кто-то сталкивался с такой проблемой? Как лучше решать?
     
  2. arrowcircle

    arrowcircle Новичок

    Сообщения:
    23
    Симпатии:
    1
    Читайте внимательнее, память не кончилась, а фрагментирована.
    Если нечего сказать по делу - лучше не писать, кмк.
     
  3. arrowcircle

    arrowcircle Новичок

    Сообщения:
    23
    Симпатии:
    1
    я этот вывод делаю на основании информации из issue на гитхабе, а вот откуда вы такие выводы делаете мне не очень понятно. Если вывести количество свободно памяти, то там цифра почти в 2 раза больше 9 кб
     
  4. __ab__

    __ab__ Новичок

    Сообщения:
    21
    Симпатии:
    2
    вот это:
    Код (Text):
    1.  
    2. # main.py
    3. import machine, ssd1306, network, time, socket, dht
    4. from umqtt.simple import MQTTClient
    5. from ustruct import unpack
    6. from json import dumps
    7.  
    8. #import dht22 as LOGIC
    9. import meteo as LOGIC
    10.  
    11. MQTT =  '10.0.0.1'
    12.  
    13. devs = None
    14. timer = None
    15. tcnt = 0
    16.  
    17. def start():
    18.     global timer, devs
    19.  
    20.     class Devs:
    21.         def __init__(self):
    22.             self.i2c = machine.I2C(scl=machine.Pin(5), sda=machine.Pin(4))
    23.             self.oled = ssd1306.SSD1306_I2C(128, 32, self.i2c, 0x3c)
    24.             self.wlan = network.WLAN(network.STA_IF)
    25.             if self.wlan.active():
    26.                 x = 0
    27.                 while x < 10 and not self.wlan.isconnected():
    28.                     time.sleep(1)
    29.             self.set_ntp_time()
    30.             from ubinascii import hexlify
    31.             self._client_id = b"esp8266_" + hexlify(machine.unique_id())
    32.             self.mqtt = None
    33.             self._fdir = {
    34.                 'IP': self.IP,
    35.                 'DATE': self.DATE,
    36.                 'TIME': self.TIME,
    37.                 'MQTT': 'MQTT Disconnected'
    38.             }
    39.  
    40.         def init_mqtt(self, broker, topic):
    41.             if not (self.mqtt is None):
    42.                 self.mqtt.disconnect()
    43.                 self.mqtt = None
    44.             try:
    45.                 self.mqtt = MQTTClient(self._client_id, broker)
    46.                 self.mqtt.connect()
    47.                 self.mqtt.topic = topic
    48.             except:
    49.                 self.mqtt = None
    50.             self._fdir['MQTT'] = 'MQTT Error!' if self.mqtt is None else MQTT + ' MQTT'
    51.  
    52.         def client_id(self):
    53.             return self._client_id.decode()
    54.  
    55.         def IP(self):
    56.             return self.wlan.ifconfig()[0] if self.wlan.isconnected() else 'Not connected'
    57.  
    58.         def DATE(self):
    59.             tm = list(time.localtime()[0:3])
    60.             for i, x in enumerate(tm):
    61.                 x = str(x)
    62.                 tm[i] = x if len(x) > 1 else '0' + x
    63.             return '%s-%s-%s' % tuple(tm)
    64.  
    65.         def TIME(self):
    66.             tm = list(time.localtime()[3:6])
    67.             for i, x in enumerate(tm):
    68.                 x = str(x)
    69.                 tm[i] = x if len(x) > 1 else '0' + x
    70.             return '%s:%s:%s' % tuple(tm)
    71.  
    72.         def screen(self, data):
    73.             if self.oled is None: return
    74.  
    75.             if type(data) != list:
    76.                 data = str(data).split('\n')
    77.  
    78.             self.oled.fill(0)
    79.             y = 0
    80.             for s in data:
    81.                 s = str(s)
    82.                 for x in self._fdir:
    83.                     if ('{{ %s }}' % x) in s:
    84.                         if type(self._fdir[x]) == str:
    85.                             s = s.replace('{{ %s }}' % x, self._fdir[x])
    86.                         else:
    87.                             s = s.replace('{{ %s }}' % x, self._fdir[x]())
    88.                 self.oled.text(s, 0, y)
    89.                 y += 10
    90.             self.oled.show()
    91.  
    92.         def set_ntp_time(self):
    93.             if self.wlan is None or not self.wlan.isconnected(): return
    94.             NTP_QUERY = bytearray(48)
    95.             NTP_QUERY[0] = 0x1b
    96.             addr = socket.getaddrinfo("pool.ntp.org", 123)[0][-1]
    97.             s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    98.             s.settimeout(1)
    99.             msg = s.sendto(NTP_QUERY, addr)
    100.             msg = s.recv(48)
    101.             s.close()
    102.             val = unpack("!I", msg[40:44])[0]
    103.             val -= 3155673600
    104.             tm = time.localtime(val + 3600 * 3)
    105.             tm = tm[0:3] + (0,) + tm[3:6] + (0,)
    106.             machine.RTC().datetime(tm)
    107.             return tm
    108.  
    109.         def publish(self, message, topic=None):
    110.             if self.mqtt is None:
    111.                 return
    112.             if type(message) == dict:
    113.                 message = {
    114.                     "Sensor": self.client_id(),
    115.                     "Data": message,
    116.                     "Time": self.DATE() + ' ' + self.TIME(),
    117.                     "IP": self.IP()
    118.                 }
    119.                 message = dumps(message)
    120.             else:
    121.                 message = str(message)
    122.             topic = self.mqtt.topic if topic is None else topic
    123.             self.mqtt.publish(topic, message)
    124.  
    125.     if devs is None:
    126.         devs = Devs()
    127.         topic, period = LOGIC.setup(devs)
    128.         devs.init_mqtt(MQTT,topic)
    129.  
    130.         def handler(timer):
    131.             global tcnt, devs
    132.             tcnt += 1
    133.             if tcnt>500:
    134.                 devs.set_ntp_time()
    135.                 tcnt = 0
    136.             msg = LOGIC.loop(devs)
    137.             if not msg is None:
    138.                 devs.publish(msg)
    139.  
    140.         timer = machine.Timer(0)
    141.         timer.init(period=period, mode=machine.Timer.PERIODIC, callback=handler)
    плюс вот это:
    Код (Text):
    1.  
    2. # meteo.py
    3. import dht, machine
    4. from bmp180 import BMP180
    5.  
    6. switch = None
    7. ext_sensor = None
    8. water_sensor = None
    9. int_sensor = None
    10. tms = 0
    11. int_alt = None
    12.  
    13. def setup(devs):
    14.     global switch, ext_sensor, water_sensor, int_sensor
    15.     switch = machine.Pin(14,machine.Pin.PULL_UP)
    16.     ext_sensor = dht.DHT22(machine.Pin(12))
    17.     water_sensor = machine.ADC(0)
    18.     int_sensor = BMP180(devs.i2c)
    19.     int_sensor.oversample_sett = 2
    20.     int_sensor.baseline = 101325
    21.  
    22.     return 'house/wether', 1000
    23.  
    24. def str2(v):
    25.     v = str(v)
    26.     if '.' in v:
    27.         v = v.split('.')
    28.         v = v[0] + '.'+ v[1][:2]
    29.     return v
    30.  
    31. def loop(devs):
    32.     global switch, ext_sensor, water_sensor, int_sensor, tms, int_temp, int_press, int_alt
    33.  
    34.     ret = None
    35.     rd =water_sensor.read()
    36.  
    37.     if tms == 0:
    38.         ext_sensor.measure()
    39.  
    40.         int_temp  = int_sensor.temperature
    41.         int_press = int_sensor.pressure * 0.00750062
    42.         int_alt   = int_sensor.altitude
    43.  
    44.         ret = {
    45.             "Temp":int_temp,
    46.             "Pressure":int_press,
    47.             "Altitude":int_alt,
    48.             "TempExt":ext_sensor.temperature(),
    49.             "mmHgExt":ext_sensor.humidity(),
    50.             "Rain":rd
    51.         }
    52.  
    53.         tms = 61
    54.  
    55.     tms -= 1
    56.     if switch()==1:
    57.         devs.screen([
    58.             '%s C %s ' % ( ext_sensor.temperature(), ext_sensor.humidity() ) + '%',
    59.             '%s C %s mmHg' % ( str2(int_temp), str2(int_press)),
    60.             'rain' if rd<900 else 'dry'
    61.         ])
    62.     else:
    63.         devs.screen([
    64.             '{{ IP }}',
    65.             '{{ DATE }} {{ TIME }}',
    66.             '{{ MQTT }}'
    67.         ])
    68.  
    69.     return ret
    cутками работает на ESP и никуда не падает ;)
    поэтому я бы предложил поискать в BME280, а для начала проверить все то же, но на другом контроллере.
    и while я бы заменил на таймер (как и сделал), дабы мусор не копился в памяти

    таймер во фрагменте main.py:
    Код (Text):
    1. def handler(timer):
    2.             global tcnt, devs
    3.             tcnt += 1
    4.             if tcnt>500:
    5.                 devs.set_ntp_time()
    6.                 tcnt = 0
    7.             msg = LOGIC.loop(devs)
    8.             if not msg is None:
    9.                 devs.publish(msg)
    10.  
    11. timer = machine.Timer(0)
    12. timer.init(period=period, mode=machine.Timer.PERIODIC, callback=handler)
     
    Последнее редактирование: 30 окт 2018
  5. arrowcircle

    arrowcircle Новичок

    Сообщения:
    23
    Симпатии:
    1
    Спасибо, попробую.
    while из базового примера, который отказался работать.

    PS. Почистил еще раз флеш, перезалил прошивку, залил скомпилированный bme280.mpy и вроде все завелось. По памяти не вылетает
     
    Последнее редактирование: 31 окт 2018
  6. arrowcircle

    arrowcircle Новичок

    Сообщения:
    23
    Симпатии:
    1
    А как Вы работает с микропитоном? Как заливаете файлы? Есть ли какой-то удобный способ заливки и отладки? Makefile или что-то подобное?
     
  7. __ab__

    __ab__ Новичок

    Сообщения:
    21
    Симпатии:
    2
    Есть такая удобная штука WEBREPL. Как ставить описано на сайте микиропитона.
    Отладка, как правило, заливкой в контроллер и проверкой как работает.
    Опять же, через REPL.
    Делал попытки написать модули - заглушки подо все и через это получить отладку в PyCharm, но пока некогда и лень ;)

    Кстати, Webrepl работает параллельно с тем кодом, что я привел выше - никаких проблем с памятью.
     
  8. arrowcircle

    arrowcircle Новичок

    Сообщения:
    23
    Симпатии:
    1
    WebRepl - это адски неудобный инструмент.
    Заливать файлы по одному - это ужас. К тому же после ребута соединение рвется.
    В идеале иметь мейкфайл/интеграцию с иде для проверки синтаксиса, прекомпиляции всего, кроме main.py, заливки всех изменившихся файлов, запуск тестов и тд.
     
  9. __ab__

    __ab__ Новичок

    Сообщения:
    21
    Симпатии:
    2
    для заливки в промышленных масштабах - возможно.
    но там рядом в репозитории есть webrepl_cli.py им без проблем можно лить из командной строки или из скрипта.
     
  10. gwvsol

    gwvsol Новичок

    Сообщения:
    5
    Симпатии:
    0
    Я сейчас разрабатываю управление системой электрического отопления.
    После того как добавил библиотеки для работы с LCD по шине i2c, так же библиотеку для RTC DS3231, DS18B20 и uasyncio (uasyncio не работает без collections) и дописал свою для обновление времени с NTP сервера и по временным зонам у меня появилось примерно такое сообщение.
    uasyncio и collections забрали под себя почти всю память.
    Решение проблемы: перенести все библиотеки на флеш. Для этого необходимо собрать свой бинарник micropython с уже добавленными в него библиотеками.
    Это не сложно. Нужно установить среду esp-open-sdk, скачать исходники micropython, добавить к ним нужные библиотеки и скомпилить все. Не могу как все это работает под Windows, под Linux не возникло никаких сложностей. Самое длительное из этого процесса, сборка из исходников esp-open-sdk.
    После сборки бинарника заливаем его на ESP8266 и заливаем исходник. А еще лучше скомпилить файл проекта через MicroPython cross compiler и залить их в скомпилированном виде. (вообще это наверное первое что нужно сделать если наблюдается нехватка памяти, скомпилить все и не заставлять контроллер заниматься компилированием).
    Внимание: файлы boot.py и main.py компилить нельзя! Они должны быть как есть с расширением py.
    Если же весь проект писать в файл main.py тогда лишаемся возможности компилировать свой проект. Вообще в файле main.py должна быть только одна строка для импорта проекта. А сам проект вынести в отдельный файл, который можно с случае необходимости скомпилить.
     
  11. __ab__

    __ab__ Новичок

    Сообщения:
    21
    Симпатии:
    2
    В micropython тоже все сделано и скрипты можно хранить в ФС на внутренней флеш. А для динамической загрузки в любом питоне есть __import__ в коде или import в REPL.
    Можно перейти на C, но на языках более высокого уровня все пишется и отлаживается быстрее.
    Если при этом все работает - зачем переходить на низкий? Ну разве если в том у кого свое удовольствие...
    То что я писал выше работает не будучи вшитым ни в какую прошивку, хотя там тоже есть и работа с экраном и NTP(одна функция в main) и MQTT.
    Я не стал плодить файлов и вписал общую для разных проектов функциональность в main.
    boot.py у меня выглядит всегда так:
    Код (Text):
    1. import gc, webrepl
    2.  
    3. webrepl.start()
    4. gc.collect()
    5.  
    6. import main
    7. main.start()
    Импорт main после webrepl на случай если при правке main там будет косяк. Соответственно можно зайти в webrepl и посмотреть что пошло не так.
    В самом main, при этом, практически нет кода, который бы выполнялся при его импорте.
     
  12. gwvsol

    gwvsol Новичок

    Сообщения:
    5
    Симпатии:
    0
    WebRepl - интересный инструмент, но согласен не удобный. Чтобы видеть что происходит сразу после reboot я использую подключение через COM порт. Я использую программу minicom для Windows есть Putty.
    А дальше все просто, расставляешь по программе вывод информации обычным print, делаешь reboot и видишь что происходит. Чтобы увидеть сколько доступно всего и сколько свободной памяти при работающем коде необходимо импортировать
    import gc
    и использовать:
    gc.mem_alloc() - для вывода сколько всего памяти
    gc.mem_free() - для вывода сколько свободной памяти
    upload_2018-11-8_7-48-27.png
    Вот так это выглядит у меня мониторинг работы через minicom
     
  13. __ab__

    __ab__ Новичок

    Сообщения:
    21
    Симпатии:
    2
    Я под Linux использую picocom, когда включаю через USB. Но чаще WebRepl - устройства то не у компа ;) И аптайм уже месяцами исчисляется...
    WebRepl тот же minicom по сути, только с возможностью загрузки файлов. C print и всем прочим - всё так же:
     

    Вложения:

    Последнее редактирование: 9 ноя 2018

Поделиться этой страницей