Всем доброго дня!
Пишу программку под прибор измерения высоты (альтиметр). Суть его в следующем: 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);
}