Моя петля Arduino перестает работать после нескольких циклов

У меня есть arduino nano, который подключен к 6-осевому датчику GY-521 и датчику температуры и влажности DHT22, который также подключен к raspberry pi в качестве ведомого через SPI. Работает нормально в течение нескольких циклов, но затем цикл останавливается, и я не знаю, почему. После поиска я понял, что это было из-за того, что раньше я использовал строку для объединения всех данных, но она все равно останавливается даже после того, как я переключился на массив символов.

Вот мой код:

#include <DHT.h>
#include <DHT_U.h>
#include <Wire.h>
#include <MPU6050_light.h>
#include <SPI.h>
#include <stdlib.h>

// определяем константы
#define LED_PIN PIN_A1
#define DHTPIN PIN7
#define DHTTYPE DHT22
#define SLAVE_ADDRESS 0x04
#define SS PIN_SPI_SS
#define MOSI PIN_SPI_MOSI
#define MISO PIN_SPI_MISO
#define SCLK PIN_SPI_SCK
#define MAX_LENGTH 42

// инициализируем объект DHT22 и MPU6050 здесь
DHT dht(DHTPIN, DHTTYPE);
MPU6050 mpu(Wire);

// определяем переменные
char cdata[MAX_LENGTH];
unsigned long timer = 0;
bool blinkState = false;
volatile byte pos;
volatile bool processed;
float hum, temp, acc[3];
char chum[7], ctemp[7], cacc0[7], cacc1[7], cacc2[7];

void mpu_setup()
{
  // Инициализировать устройство
  byte status = mpu.begin();
  Serial.println(F("MPU6050 status: "));
  Serial.println(status);

  // остановить все, если датчик не в порядке
  while (status != 0)
  {
  }

  Serial.println(F("Calculating offsets, do not move MPU6050..."));

  // ждем готовности
  delay(1000);

  mpu.calcOffsets(false, true);
}

void dht_setup()
{
  dht.begin();
}

void setup()
{
  Serial.begin(115200);

  Wire.begin();
  Wire.setClock(400000); // Часы I2C 400 кГц. Прокомментируйте эту строку, если у вас возникли трудности с компиляцией
  Serial.println("Begin setup");
  dht_setup();
  Serial.println(F("Done dht setting."));
  mpu_setup();
  Serial.println(F("Done MPU setting."));

  // эти результаты в SPCR = 0b01000000
  SPCR |= (1 << SPE);
  SPI.attachInterrupt();

  // установить режим вывода в ведомом режиме
  pinMode(MISO, OUTPUT);
  pinMode(MOSI, INPUT);
  pinMode(SCLK, INPUT);
  pinMode(SS, INPUT);

  pinMode(LED_PIN, OUTPUT); // установка вывода светодиода для вывода

  pos = 0;
  processed = false;
  Serial.println(F("Starting program."));
}

// Процедура прерывания SPI
// вызывается каждый цикл передачи SPI.
ISR(SPI_STC_vect)
{
  byte b = SPDR;

  // Первый стартовый байт, полученный от мастера.
  // Это указывает на начало транзакции SPI.
  if (b == 0x10)
  {
    pos = 0;
    processed = true;
  }

  if (pos < MAX_LENGTH)
  {
    SPDR = cdata[pos++];
  }
  else
  {
    processed = false;
  }
}

void loop()
{
  // получаем данные с датчика, когда не обрабатывается вызов SPI
  if (!processed)
  {
    mpu.update();
  }
  if (millis() - timer > 1000) // обновление светодиода каждую секунду
  {
    hum = dht.readHumidity();
    temp = dht.readTemperature();

    acc[0] = mpu.getAccX();
    acc[1] = mpu.getAccY();
    acc[2] = mpu.getAccZ();

    dtostrf(hum, 2, 2, chum);
    dtostrf(temp, 2, 2, ctemp);
    dtostrf(acc[0], 2, 2, cacc0);
    dtostrf(acc[1], 2, 2, cacc1);
    dtostrf(acc[2], 2, 2, cacc2);
    // раньше было
    // Строковые данные = "|h=" + Строка(гул) + "|t=" + Строка(временная) + "|x=" + String(acc[0]) + ",y=" + String(acc[1]) + ",z=" + String(acc[2]) + "|";
    // strcopy(cdata, data);
    sprintf(cdata, "|h=%s|t=%s|x=%s,y=%s,z=%s|", chum, ctemp, cacc0, cacc1, cacc2);
    Serial.println(cdata);

    // мигает, указывая на то, что программа запущена
    blinkState = !blinkState;
    digitalWrite(LED_PIN, blinkState);
    timer = millis();
  }
}

Из приведенного выше кода, когда цикл останавливается, я знаю, что светодиод перестает мигать, но передача SPI работает нормально, просто данные не обновляются.

, 👍0

Обсуждение

sprintf(cdata,..... cdata имеет значение 42, которое выглядит довольно маленьким в качестве буфера для всего этого форматированного вывода. Использование snprintf() безопаснее., @6v6gt

@ 6v6gt Извините, я новичок в программировании на C. Как snprintf() может быть безопаснее, чем sprintf()? Означает ли это, что sprintf() может иногда записывать больше содержимого в указанный буфер?, @Rian Wardana

Проблема в том, что легко ошибиться при вычислении окончательного размера таких отформатированных строк. Ошибитесь, и приемный буфер может переполниться. С помощью snprintf() вы можете указать размер буфера, чтобы предотвратить переполнение. Но в любом случае, просто чтобы это заработало, увеличьте буфер, скажем, до 50 символов. Также убедитесь, что функции dtostrf() не являются источником переполнения, как это могло бы быть, если бы они передавали большие числа с плавающей запятой., @6v6gt

@ 6v6gt на самом деле числа с плавающей запятой, подаваемые в dtostrf(), представляют собой фиксированную цифру, скажем, температура всегда будет комнатной температуры в градусах Цельсия, может быть, от -15,00 до 70,00; акселерометр (accX) нормализован в диапазоне от -0,99 до 1,99; это не более 6 символов, поэтому 7, включая \0 char в конце, чтобы сделать его строковым. Я увеличу общий размер буфера и воспользуюсь snprintf(), чтобы посмотреть, смогу ли я заставить эту программу работать вечно., @Rian Wardana