Проблема с работой SIM800L

Всем доброго дня!
Пишу программку под прибор измерения высоты (альтиметр). Суть его в следующем: Arduino NANO собирает данные от датчика BMP280 и отображает текущий уровень высоты на экране (относительно предварительно заданного уровня). При этом, т.к. атмосферное давление постоянно изменяется, отдельный прибор мерит фон изменения давления и отправляет поправки изменения высоты через GSM модуль на носимый прибор в формате “2.5”.
В ходе тестирования возникла проблема: модуль SIM800L жутко глючный и требовательный к питанию. При работе от аккума экран и датчик постоянно отваливаются, а контроллер зависает. Сбои происходят именно в момент установления связи модулем и в момент приема сообщений. Почему-то программа полностью зависает.
При этом, самый странный момент в следующем: при подключении модуля к компьютеру по USB, модуль работает исправно и присылает команды в serial порт! Сразу возникает идея о том что проблема в стабильном питании SIM800L, а USB как раз обеспечивает стабильные 5V. Но не тут то было: я воткнул USB в блок зарядки (а не в комп), и проблема вновь проявилась! Сам прибор питаю от литиевого 18650 напрямую, без всяких преобразователей. Даже повесил конденсатор 470мкФ параллельно питанию SIM800L: все без толку. При этом, каких-то блокирующих функций при выводе в serial порт нет. Пробовал закомментировать все выводы в serial: никакого эффекта.
Может, у кого появятся идеи, в чем может быть проблема?

P.S. В программировании я новичок

#include <Wire.h>   
#include <GyverOLED.h>
#include <GyverBME280.h>
#include <EEPROM.h>
#include <GyverFilters.h>
#include <SoftwareSerial.h>
#include <GyverButton.h>
// === Настройки пинов и параметров ===
#define OLED_RESET    -1
#define but1           2
#define but2           3
#define but3           4

#define longpress      3000
#define screen_ref     300

GyverOLED<SSD1306_128x64> display;
GyverBME280 bmp;
GFilterRA Running;
GFilterRA Press_filter;
GFilterRA Temp_filter;
SoftwareSerial sim800(5, 6);  // RX, TX

GButton btn1(but1);
GButton btn2(but2);
GButton btn3(but3);

// === Переменные ===
unsigned long now_millis, last_millis;
byte num_ekr = 0;

float pressure_zero = 1005.00;
float temp_zero = 20.00;
float altitude_offset = 0.0;
float lastGoodAltitude = 0;

float pressure_now = 0, temp_now = 0;

// === Буфер для SMS ===
char incomingData[8];
byte incomingIndex = 0;
bool smsReady = false;
bool smsProcessed = false;
volatile bool simReceiving = false;
unsigned long lastSMSTime = 0;

#define SERIAL_SAFE_PRINT(x) if (Serial) Serial.print(x)
#define SERIAL_SAFE_PRINTLN(x) if (Serial) Serial.println(x)

float calcAltitude(float seaLevelhPa, float pressure, float temperature);
void sendCommand(String cmd);
void parseSMS();

void setup() {
  Serial.begin(9600);
  sim800.begin(9600);

  sim800.println("AT+CMGF=1");     // Текстовый режим
  delay(500);
  sim800.println("AT+CNMI=1,2,0,0,0"); // Автоприем SMS
  delay(500);

  Running.setCoef(0.15);
  Press_filter.setCoef(0.3);
  Temp_filter.setCoef(0.5);

  btn1.setDebounce(50);
  btn2.setDebounce(50);
  btn3.setDebounce(50);
  btn2.setTimeout(longpress);

  pinMode(but1, INPUT_PULLUP);
  pinMode(but2, INPUT_PULLUP);
  pinMode(but3, INPUT_PULLUP);

  EEPROM.get(0, pressure_zero);
  EEPROM.get(10, altitude_offset);
  EEPROM.get(20, temp_zero);

  if (!bmp.begin()) while (1);  // Ошибка BME280

  display.init();
  display.setContrast(255);
  display.clear();
  display.update();
  display.setScale(1);
  display.setCursorXY(30, 30);
  display.print(F("Loading..."));
  display.update();
  delay(500);

  pressure_now = bmp.readPressure();
  temp_now = bmp.readTemperature();
}

void loop() {
  now_millis = millis();
  btn1.tick();
  btn2.tick();
  btn3.tick();

  pressure_now = bmp.readPressure();
  temp_now = bmp.readTemperature();

  if (btn1.isClick()) num_ekr = (num_ekr + 1) % 4;

  if (btn2.isHolded()) {
    pressure_zero = Press_filter.filtered(pressure_now);
    temp_zero = Temp_filter.filtered(temp_now);
    EEPROM.put(0, pressure_zero);
    EEPROM.put(20, temp_zero);

    display.clear();
    display.setScale(2);
    display.setCursorXY(0, 0);
    display.print(F("Калибровка..."));
    display.update();
    delay(500);
  }

  if (btn2.isClick()) {
    altitude_offset += 0.5;
    EEPROM.put(10, altitude_offset);
  }

  if (btn3.isClick()) {
    altitude_offset -= 0.5;
    EEPROM.put(10, altitude_offset);
  }

  if (now_millis - last_millis > screen_ref) {
    last_millis = now_millis;

    if (isnan(pressure_now) || pressure_now <= 0 || isnan(temp_now) || temp_now < -100 || temp_now > 100) {
      SERIAL_SAFE_PRINTLN(F("⚠️ Обнаружена ошибка BME280. Перезапуск..."));
      delay(1000);
      if (!bmp.begin()) {
        SERIAL_SAFE_PRINTLN(F("❌ BME280 не ответил после перезапуска"));
      } else {
        SERIAL_SAFE_PRINTLN(F("✅ BME280 успешно перезапущен"));
        pressure_now = bmp.readPressure();
        temp_now = bmp.readTemperature();
      }
    }

    display.clear();
    switch (num_ekr) {
      case 0:
        display.setScale(1);
        display.setCursorXY(40, 0);
        display.print(F("Альтметр"));
        display.setCursorXY(0, 12);
        display.print(F("P: "));
        display.print(Press_filter.filtered(pressure_now), 1);
        display.setCursorXY(0, 24);
        display.print(F("Alt: "));
        display.print(Running.filtered(calcAltitude(pressure_zero, pressure_now, temp_zero)) + altitude_offset, 1);
        display.setCursorXY(0, 36);
        display.print(F("T: "));
        display.print(temp_now, 1);
        display.setCursorXY(0, 48);
        display.print(F("Corr: "));
        display.print(altitude_offset, 1);
        break;

      case 1:
        display.setScale(2);
        display.setCursorXY(0, 0);
        display.print(F("Давление"));
        display.setCursorXY(0, 20);
        display.print(Press_filter.filtered(pressure_now), 1);
        break;

      case 2:
        display.setScale(2);
        display.setCursorXY(0, 0);
        display.print(F("Высота"));
        display.setScale(1);
        display.setCursorXY(75, 5);
        display.print(F("(фильтр.)"));
        display.setScale(2);
        display.setCursorXY(30, 25);
        display.print(Running.filtered(calcAltitude(pressure_zero, pressure_now, temp_zero)) + altitude_offset, 1);
        display.setScale(1);
        display.setCursorXY(0, 48);
        display.print(F("Corr: "));
        display.print(altitude_offset, 1);
        break;

      case 3:
        display.setScale(2);
        display.setCursorXY(0, 0);
        display.print(F("Высота "));
        display.setCursorXY(30, 25);
        display.print(calcAltitude(pressure_zero, pressure_now, temp_zero) + altitude_offset, 1);
        display.setScale(1);
        display.setCursorXY(0, 48);
        display.print(F("Corr: "));
        display.print(altitude_offset, 1);
        break;
    }

    display.update();

  }

  // Обработка SMS только один раз и только если не идёт приём
  if (smsReady && !smsProcessed && !simReceiving) {
    parseSMS();
    smsProcessed = true;
  }
}

// === Прерывание — асинхронный приём SMS ===
void serialEvent() {
  while (sim800.available()) {
    char c = sim800.read();
    simReceiving = true;

    if (c == '\r' || c == '\n') {
      if (incomingIndex >= 1) {
        incomingData[incomingIndex] = '\0';

        bool valid = true;
        for (byte i = 0; i < incomingIndex; i++) {
          if (!isDigit(incomingData[i]) && incomingData[i] != '.' && incomingData[i] != '-') {
            valid = false;
            break;
          }
        }

        if (valid) {
          smsReady = true;
          smsProcessed = false;
          lastSMSTime = millis();
        }
      }

      incomingIndex = 0;
      simReceiving = false;
    } else if (incomingIndex < sizeof(incomingData) - 1) {
      incomingData[incomingIndex++] = c;
    } else {
      incomingIndex = 0;
      simReceiving = false;
    }
  }
}

// === Обработка готового SMS ===
void parseSMS() {
  float newOffset = atof(incomingData);

  SERIAL_SAFE_PRINT(F("[parseSMS] Входящее значение: "));
  SERIAL_SAFE_PRINTLN(incomingData);

  if (newOffset > -1000 && newOffset < 10000 && newOffset != altitude_offset) {
    altitude_offset = newOffset;
    EEPROM.put(10, altitude_offset);
    SERIAL_SAFE_PRINT(F("✅ Новая коррекция: "));
    SERIAL_SAFE_PRINTLN(altitude_offset);
  } else {
    SERIAL_SAFE_PRINTLN(F("❌ Некорректное или повторное значение"));
  }

  sendCommand("AT+CMGD=1,4");

  incomingData[0] = '\0';
  incomingIndex = 0;

  delay(1000);
  if (!bmp.begin()) {
    SERIAL_SAFE_PRINTLN(F("❌ Ошибка BME280 после SMS"));
  } else {
    SERIAL_SAFE_PRINTLN(F("✅ BME280 перезапущен после SMS"));
    float testPressure = bmp.readPressure();
    float testTemp = bmp.readTemperature();
    SERIAL_SAFE_PRINT(F("[BMP280] Pressure: ")); SERIAL_SAFE_PRINT(testPressure);
    SERIAL_SAFE_PRINT(F(" | Temp: ")); SERIAL_SAFE_PRINTLN(testTemp);
  }
}

// === Расчёт высоты ===
float calcAltitude(float pressure0, float pressure, float temperature) {
  const float coeff = 29.261;

  if (isnan(pressure) || isnan(pressure0) || isnan(temperature) ||
      pressure <= 0 || pressure0 <= 0 || temperature < -100 || temperature > 100) {
    SERIAL_SAFE_PRINTLN(F("⚠️ calcAltitude: NaN или неверные входные данные"));
    return lastGoodAltitude;
  }

  temperature += 273.15;
  pressure /= 100.0;
  pressure0 /= 100.0;

  float result = -coeff * temperature * log(pressure / pressure0);

  if (isnan(result)) {
    SERIAL_SAFE_PRINTLN(F("⚠️ calcAltitude: результат NaN"));
    return lastGoodAltitude;
  }

  lastGoodAltitude = result;
  return result;
}

// === Отправка команды SIM800 ===
void sendCommand(String cmd) {
  sim800.println(cmd);
}

Я бы у гайвера спросил

4 лайка

а вы случайно в космос не собрались запускать ?))) а то BMP280 вроде как работает на максимальной высоте 10-15км если не ошибаюсь… возможно это вам сэкономит время…
так же можете попытаться запитать нано через зарядку которая выдает ток 2+ ампер, и убедитесь что кабель столько же способен пропускать… может это поможет…

SIM800 требует питание в пределах 4,1-4,3В, при выходе за эти пределы сильно глючит, горит, или тупо не работает. В пике до 2А при установлении связи потребляет.

Добавьте параллельно ещё один 18650 для проверки.

ДШ утверждает несколько иное, но верить нужно кончено же тебе.

1 лайк

Нет, для наземного использования

Если тебе не очевидно, намекну иначе - для начала разберись с питанием.
К примеру, запитай всё от блока питания (желательно лабораторного, но не обязательно), который может выдавать 5В с током 2А. Если проблема уйдет - значит что-то другое. А если нет… Значит виноват метиорит (или эмв солнца).

По-любому уйдёт :smiley: Вместе с волшебным дымом на котором работают все микросхемы. Вопрос во времени. SIM создан питаться от лития с соответствующими параметрами. Ежели @andycat придёт, то, может, подскажет. Он на них кореец.

То есть мозги уже сразу исключаем?
Тогда я пасс…

@Semyon а какой у вас делитель на RX стоит?

Ставлю на то, что аккумулятор + зарядка не тянет стартовый ток.
Правильно - > подключить именно напрямую!!! к аккумулятору.
Зярядку оставить как есть.
ЗЫ. Буквально сейчас гоняю в бою железку sim800l + stm32f411ceu6. На работе, когда тестировал все было отлично, куча смс, звонков, тестов асе было отлично. Принёс домой, начались проблемы. Мк тупо завис через 10 часов работы. Добавил wdt, как бы помогло но это не решение.
Стал разбираться - дома зона сотовой сети отвратительная (
Уровень всего 10 из 30 2G. А на работе 24 в среднем.
И да, стоит зарядка tp4056.
Завтра понесу на работу, все нафиг перепаивать.

  • поставить после повышайки (после акк) конденсатор 470 мкф.
1 лайк

Вот такое подключение уже три года работает и все отлично.

1 лайк

А вот так подключать не надо как оказалось

1 лайк

от тебя ничего не скрыть, однако где ты видишь противоречие между тем, что я написал, и датащитом?

он от 5В должен тихо умереть, по идее, даташит выше товарищ привел

Если ты не видишь суслика это не значит, что его нет.

напиши мне в личку фразу, которую мне надо написать в ответ на твои претензии, чтоб ты был доволен.

Ты сморозил глупость а теперь пытаешься отмазаться. Поэтому. Велком ту игноре лист.

Я о характеристиках блока питания говорил, ну и о мозгах чуть ниже сообщил.

1 лайк