Arduino зависает (вероятно, из-за I2C). Нужна помощь в написании надежного сценария.
Arduino Pro mini 3,3 В зависает через несколько часов после запуска. После повторного включения он начинает зависать через несколько секунд. Если я подожду достаточно долго, прежде чем включить его, он зависнет немного позже, например, теперь это длилось 45 секунд, прежде чем зависло. Всякий раз, когда он застревает, на выводе SCL высокий уровень, а на выводе SDA низкий уровень, поэтому я подозреваю, что что-то не так с I2C. При запуске происходит обратный отсчет в 60 секунд, и там обычно происходит зависание. Вы увидите, что это чрезвычайно простой цикл for. В проекте есть датчик SHT31, OLED-экран с управлением SH1106 128x64, модуль часов DS3231, датчик hc-sr501PIR, LDR, кодировщик и кнопка. Только первые три работают с I2C, и код для этих устройств очень короткий, они находятся в "start(), updatescreen(), getTime(), getDew()". функции в длинном сценарии ниже, поэтому я надеюсь, что диагностировать проблему не составит труда. Я провел несколько экспериментов. Я изолировал SHT31 и OLED, оба вызвали зависание, хотя все остальные устройства были отключены (я добавил код мигания в их пример тестового скетча, и мигание прекратилось через несколько секунд, что означает зависание). OLED-экран работает, когда он подключен с помощью перемычек за пределами печатной платы, поэтому я даже подозревал, что это печатная плата, но он настолько прост, что не должен вызывать проблем. Самое странное, что это устройство прекрасно проработало 2 дня, прежде чем впервые сломалось.
Схема:
Печатная плата (некоторые ошибки были исправлены позже):
Реальные изображения:
Тестовый скрипт (возникает та же проблема с зависанием, но его легче читать):
#include <U8g2lib.h>
#include <Wire.h>
U8G2_SH1106_128X64_NONAME_1_HW_I2C u8g2(U8G2_R0, /* reset=*/U8X8_PIN_NONE);
void setup() {
u8g2.begin();
u8g2.setFont(u8g2_font_ncenB14_tr);
pinMode(LED_BUILTIN, OUTPUT);
}
void loop() {
u8g2.firstPage();
do {
u8g2.setCursor(0, 20);
u8g2.print(F("Hello World!"));
} while (u8g2.nextPage());
delay(1000);
digitalWrite(LED_BUILTIN, HIGH); // включаем светодиод (HIGH - уровень напряжения)
delay(1000); // подождем секунду
digitalWrite(LED_BUILTIN, LOW);
}
Тестовый сценарий 2 (на этот раз для sht31 возникает та же проблема с зависанием, но его легче читать):
#include <Wire.h>
#include "ClosedCube_SHT31D.h"
ClosedCube_SHT31D sht3xd;
void setup() {
Wire.begin();
sht3xd.begin(0x44);
pinMode(LED_BUILTIN, OUTPUT);
}
void loop() {
digitalWrite(LED_BUILTIN, HIGH);
SHT31D result = sht3xd.readTempAndHumidity(SHT3XD_REPEATABILITY_LOW, SHT3XD_MODE_CLOCK_STRETCH, 50);
delay(1000);
digitalWrite(LED_BUILTIN, LOW);
delay(1000);
}
Полный сценарий:
#include "Wire.h"
#include "ClosedCube_SHT31D.h"
#include <DS3231.h>
#include "LowPower.h"
#include <EEPROM.h>
#include <U8g2lib.h>
#define PIR 2
#define ENCODER_BUTTON 3
#define ENC_A 4
#define ENC_B 5
#define FAN_MAIN_REL 8
#define FAN_SECOND_REL 9
#define BUZZER 10
#define LIGHT_REL 11
#define LDR_PWR 12
#define LDR_READ A0
#define FREE_RUN_BUTTON A2
#define CLOCK_INT A3
volatile bool PIR_flag, TIM1_flag, But_flag, FreeRun_flag, Settings_flag;
uint8_t Weather_flag; // Weather_flag является особенным, он устанавливается/сбрасывается обычной функцией, 0: роса ниже порогового значения, 1: роса превышает DewThres, но меньше, чем DewThresExtreme, 2: роса превышает DewThresExtreme
volatile int16_t encVal;
// Переменные:
float Temp, Hum, Dew;
bool DewTrigEnabled;
uint8_t FanMotTrigEnabled; // для реле вентилятора
bool LightRelEnabled; // для реле освещения
uint16_t waitDur; // Настройка 1
uint16_t Runtime; // Настройка 2
float DewThres; // Настройка 3
float DewThresExtreme;
uint16_t FreeRunDur; // Настройка 5
bool FreeRunStatus;
bool DNDenabled;
uint8_t DND_SH; // Час начала режима «Не беспокоить»
uint8_t DND_SM; // Минута начала режима «Не беспокоить»
uint8_t DND_FH; // Час завершения DND
uint8_t DND_FM; // Минута окончания режима «Не беспокоить»
uint16_t counter;
uint8_t hour, minute;
bool LDRenabled;
uint16_t LDRThres;
bool screen_awake = true; // экран запускается "вкл"
uint8_t FanRelayStatus;
const uint8_t screenTime = 60; // Время включения экрана без движения (секунды)
const float DewUSratio = 0.98; // Определяет точку росы, которую выключает ВЕНТИЛЯТОР, если она изначально была вызвана росой
const uint8_t phaseLim = 120; // Определяет скорость проверки LDR, если свет выключен, но LDR включен, каждая фаза соответствует 5 секундам.
ClosedCube_SHT31D sht3xd;
RTClib myRTC_1;
DS3231 myRTC_2;
U8G2_SH1106_128X64_NONAME_1_HW_I2C u8g2(U8G2_R0, /* reset=*/U8X8_PIN_NONE);
void swCounter(bool turn_on, uint32_t cnt = 0) {
if (turn_on) {
counter = cnt;
TCNT1 = 0; // значение счетчика TIM1
TCCR1B |= (1 << CS12) | (1 << CS10); // Запускаем таймер, установив для прескалера значение 1024
} else
TCCR1B &= ~(1 << CS12) | ~(1 << CS10); // Остановить ТИМ1
}
void setup() {
LoadPinsAndEEPROM();
Wire.begin();
// Датчик температуры и влажности:
sht3xd.begin(0x44);
// Модуль часов:
myRTC_2.setA2Time(
0, 0, 0,
0b01110000, false, false, false);
myRTC_2.turnOnAlarm(2);
myRTC_2.setA1Time(
0, 0, 0xFF, 0,
0b00001110, false, false, false);
myRTC_2.turnOffAlarm(1);
myRTC_2.checkIfAlarm(1); // очищаем флаг тревоги 1
sht3xd.heaterEnable();
// OLED-экран:
u8g2.begin();
u8g2.setFont(u8g2_font_lubB12_tr);
for (int i = 60; i > 0; i--) {
u8g2.firstPage();
do {
u8g2.drawStr(6, 25, "by Prince");
u8g2.setCursor(58, 55);
u8g2.print(i);
if (i == 30) sht3xd.heaterDisable();
} while (u8g2.nextPage());
delay(1000);
}
u8g2.setDrawColor(2);
//Конфигурация TIM1:
TCCR1A = 0;
TCCR1B = 0;
TIMSK1 |= (1 << OCIE1A); // Сравнение выходных данных и разрешение прерывания по совпадению
OCR1A = 39061; // Сравнение выходных данных A
TCCR1B |= (1 << WGM12); // CRC (таймер автоматического сброса при достижении OCR1A)
TCNT1 = 0; // значение счетчика TIM1
// Присоединяем прерывания (PinChanges и INTpin)
attachInterrupt(digitalPinToInterrupt(PIR), ISR_PIR, RISING);
attachInterrupt(digitalPinToInterrupt(ENCODER_BUTTON), ISR_ENCODER_BUTTON, FALLING);
PCICR |= (1 << PCIE1); // Группа A0-A6 может создавать прерывания
PCMSK1 |= (1 << PCINT10) | (1 << PCINT11); // Выводы A2 и A3 могут создавать прерывания
PCMSK2 |= (1 << PCINT20) | (1 << PCINT21); // Выводы D4 и D5 могут создавать прерывания
Beep(1, 100, 0);
PIR_flag = false;
But_flag = false;
}
void loop() {
checkButtons(); // После выполнения он вернется.
if (!getDNDstatus()) {
getDew(); // соответственно устанавливаем флаг
if (PIR_flag) {
if (FanMotTrigEnabled == 1) {
TIM1_flag = true; // Первоначально обновляем экран
screenPwr(1);
unsigned long lastMotion;
uint8_t step = 1;
if (waitDur <= 5) step = 2; // PIR_flag уже имеет значение true в этот момент
else swCounter(1, waitDur);
for (step = step; step <= 2; step++) { // 1: ожидание, 2: взято на охрану, 3: запуск
if (step == 2) {
(waitDur >= 90) ? counter = 60 : counter = 30; // Решаем, как долго будет проверяться движение
swCounter(1, counter);
PIR_flag = false;
updateScreen(step);
}
while (counter > 0) { // Цикл счетчика
if (checkButtons()) return;
if (PIR_flag) {
PIR_flag = false;
lastMotion = millis();
if (step >= 2) { // если он поставлен на охрану или работает и обнаружено движение
if (step == 2) { // если он поставлен на охрану и обнаружено движение
if (FanRelayStatus != 2) swFanRelays(1);
step = 3; // Движение..
updateScreen(step);
}
swCounter(1, Runtime); // Интервал перезапуска
}
}
if (TIM1_flag) {
TIM1_flag = false;
if (getDNDstatus()) return;
if (step == 1 && (millis() - lastMotion) / 1000 >= screenTime) return;
getDew();
DewControl(step != 3); // в случае, если роса увеличивается во время ожидания
updateScreen(step);
}
}
}
} else if (FanMotTrigEnabled != 1) { // Триггер движения отключен, но движение есть
PIR_flag = false;
if (FanMotTrigEnabled == 2) swFanRelays(0);
RunScreen(false);
}
}
DewControl(true);
Sleep(true);
} else { // если сейчас время «Не беспокоить»:
if (PIR_flag) {
PIR_flag = false;
RunScreen(true);
}
if (getDNDstatus()) Sleep(false); // Статус DnD проверяется повторно, поскольку он мог застрять в RunScreen пользователем после завершения режима DND.
}
}
void DewControl(bool cmd1) { // 0: невозможно выключить вентилятор; но только "вкл" 1: оба могут включить "вкл" и «выкл.»
if (Weather_flag == 0 && cmd1) swFanRelays(0);
else if (Weather_flag == 1) swFanRelays(1);
else if (Weather_flag == 2) swFanRelays(2);
}
void RunScreen(bool cmd1) { // 0: не выполнять возврат в зависимости от режима «Не беспокоить», 1: возвращать, если больше не в режиме «Не беспокоить» (предотвращает зависание в режиме «Не беспокоить», если экран включен)
TIM1_flag = true; // Первоначально обновляем экран
screenPwr(1);
swCounter(1, screenTime); // Исправлено время экрана запуска
while (counter > 0) {
if (checkButtons()) return;
if (TIM1_flag) {
if (getDNDstatus()) {
if (FanRelayStatus) return;
} else {
if (cmd1) return;
DewControl(true);
}
getDew();
updateScreen(0); // Простой или роса
if (PIR_flag) {
counter = screenTime;
PIR_flag = false;
}
TIM1_flag = false;
}
}
LightRel_IfNeeded(0);
}
void getDew() {
SHT31D result = sht3xd.readTempAndHumidity(SHT3XD_REPEATABILITY_LOW, SHT3XD_MODE_CLOCK_STRETCH, 50); // раньше это была статическая переменная
Temp = result.t;
Hum = result.rh;
float a_func = log(Hum / 100) + 17.625 * Temp / (243.04 + Temp);
Dew = (243.04 * a_func) / (17.625 - a_func);
if (!DewTrigEnabled || (Dew <= DewThres * DewUSratio) || (Dew < DewThres && FanRelayStatus == 0)) Weather_flag = 0;
else if (Dew < DewThresExtreme) Weather_flag = 1;
else Weather_flag = 2;
}
bool getDNDstatus() { // проверяем, находятся ли часы и минуты в интервале
if (!DNDenabled) return false;
updateTime();
uint16_t currentmin = hour * 60 + minute;
uint16_t localDNDstart = DND_SH * 60 + DND_SM;
uint16_t localDNDfinish = DND_FH * 60 + DND_FM;
if (localDNDstart < localDNDfinish) return (currentmin >= localDNDstart && currentmin < localDNDfinish);
else return (currentmin >= localDNDstart || currentmin < localDNDfinish);
}
void updateTime() {
if (myRTC_2.checkIfAlarm(2)) {
DateTime now; // раньше это была статическая переменная
now = myRTC_1.now();
hour = now.hour();
minute = now.minute();
}
}
void swFanRelays(uint8_t cmd1) { // 0: выключить, 1: включить
if (FanRelayStatus == cmd1) return;
bool PIR_flag_original = PIR_flag;
if (cmd1 == 0) {
digitalWrite(FAN_MAIN_REL, LOW);
digitalWrite(FAN_SECOND_REL, LOW);
FanRelayStatus = 0;
} else if (cmd1 == 1) {
digitalWrite(FAN_MAIN_REL, HIGH);
if (FanRelayStatus != 2) {
digitalWrite(FAN_SECOND_REL, HIGH);
delay(500);
}
digitalWrite(FAN_SECOND_REL, LOW);
FanRelayStatus = 1;
} else if (cmd1 == 2) {
digitalWrite(FAN_MAIN_REL, HIGH);
digitalWrite(FAN_SECOND_REL, HIGH);
FanRelayStatus = 2;
}
delay(100);
if (!PIR_flag_original) PIR_flag = false;
But_flag = false;
FreeRun_flag = false;
}
void LightRel_IfNeeded(bool cmd1) { // 0: выключить реле освещения, 1: включить реле освещения
static bool LightRelStatus;
bool LDRresult;
static uint8_t phase; // регулярно проверяйте уровень освещенности, если яркость в помещении со временем меняется.
switch (cmd1) {
case 0:
if (!LightRelStatus) return;
LightRelStatus = false;
phase = 0;
digitalWrite(LIGHT_REL, LOW);
delay(100);
PIR_flag = false;
But_flag = false;
FreeRun_flag = false;
break;
case 1:
if (!LightRelStatus && phase == 0) {
if (LDRenabled) {
digitalWrite(LDR_PWR, HIGH);
delay(10);
LDRresult = (analogRead(LDR_READ) > LDRThres); // Тьма --> Высокое сопротивление --> Высокое напряжение на LDR --> ИСТИНА Горит --> Низкое сопротивление --> Низкое напряжение на LDR --> ЛОЖЬ
digitalWrite(LDR_PWR, LOW);
} else LDRresult = true;
if (LDRresult) {
LightRelStatus = true;
digitalWrite(LIGHT_REL, HIGH);
delay(100);
PIR_flag = false;
But_flag = false;
FreeRun_flag = false;
}
}
phase++;
if (phase > phaseLim) phase = 0; // значение по умолчанию — 120, что означает 10 минут
break;
}
}
void Sleep(bool cmd1) { // 0: нормальный сон 1: сон без выключения вентилятора
if (cmd1 == 0) swFanRelays(0);
LightRel_IfNeeded(0);
screenPwr(0);
updateTime();
PIR_flag = false;
But_flag = false;
FreeRun_flag = false;
LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);
}
void updateScreen(uint8_t cmd1) { // 0: триггер простоя или росы, 1: обнаружен, 2: ожидание, 3: поставлен на охрану, 4: триггер двигателя, 5: продолжительность свободного хода
String info;
LightRel_IfNeeded(1); // Вызывается регулярно, поскольку информация о яркости комнаты обновляется каждую фазу*5/60 минут.
updateTime();
switch (cmd1) {
case 0:
if (FanRelayStatus == 0) info = "Idle";
else if (FanRelayStatus == 1) info = "Dew";
else info = "OverDew";
break;
case 1:
info = "Waiting";
if (FanRelayStatus == 1) info += "&Dew";
else if (FanRelayStatus == 2) info += "&OverDew";
break;
case 2:
info = "Armed";
if (FanRelayStatus == 1) info += "&Dew";
else if (FanRelayStatus == 2) info += "&OverDew";
break;
case 3:
info = "Motion";
if (Weather_flag == 1) info += "&Dew";
else if (Weather_flag == 2) info += "&OverDew";
break;
case 4:
info = "<";
info += String(counter / 60 + 1);
info.remove(info.indexOf('.'));
info += " mins";
break;
}
u8g2.firstPage();
do {
u8g2.setFont(u8g2_font_6x10_mr); // u8g2_font_haxrcorp4089_tn
u8g2.setCursor(0, 7);
u8g2.print(info);
u8g2.setCursor(99, 7);
if (hour < 10) u8g2.print("0");
u8g2.print(hour);
u8g2.print(":");
if (minute < 10) u8g2.print("0");
u8g2.print(minute);
u8g2.drawLine(0, 10, 128, 10);
u8g2.setFont(u8g2_font_fub20_tn);
u8g2.setCursor(0, 38);
if (Temp < 10 && Temp >= 0) u8g2.print("0");
u8g2.print(Temp, 1);
u8g2.setCursor(0, 64);
u8g2.print(Hum, 1);
u8g2.setCursor(71, 61);
if (Dew < 10) u8g2.print("0");
u8g2.print(Dew, 1);
if (Dew < DewThres) u8g2.drawFrame(70, 38, 58, 26);
else u8g2.drawBox(71, 39, 56, 24);
u8g2.setFont(u8g2_font_lubB12_tr);
u8g2.drawStr(55, 30, "*C");
u8g2.drawStr(55, 64, "%");
if (FreeRunStatus == true) u8g2.drawStr(81, 30, "FREE");
else if (getDNDstatus()) u8g2.drawStr(82, 30, "DND");
} while (u8g2.nextPage());
}
void screenPwr(bool cmd1) {
if (cmd1 == screen_awake) return;
switch (cmd1) {
case 0:
u8g2.setPowerSave(1);
u8g2.clear();
screen_awake = false;
break;
case 1:
u8g2.setPowerSave(0);
swCounter(0);
screen_awake = true;
break;
}
}
void FreeRun() {
Beep(1, 500, 0);
FreeRun_flag = false;
FreeRunStatus = true;
getDew();
swFanRelays(1);
swCounter(1, FreeRunDur);
updateScreen(4);
screenPwr(1);
while (counter > 0) {
if (But_flag) {
FreeRunStatus = false;
Settings();
return;
}
if (FreeRun_flag) {
Beep(1, 100, 0);
delay(1000);
if (!digitalRead(FREE_RUN_BUTTON)) { // если пользователь удерживает кнопку нажатой
swFanRelays(0);
Beep(2, 100, 100);
while (!digitalRead(FREE_RUN_BUTTON)) delay(100); // Чтобы пользователь удалил руку
delay(500); // предотвращаем дребезг
FreeRun_flag = false;
break;
} else {
FreeRun_flag = false;
if (FanRelayStatus == 1) swFanRelays(2);
else if (FanRelayStatus == 2) swFanRelays(1);
}
}
if (TIM1_flag) {
TIM1_flag = false;
if (getDNDstatus()) break;
getDew();
updateScreen(4);
}
}
DewControl(true);
FreeRunStatus = false;
PIR_flag = false; // Игнорировать движение в режиме "Free Run" режим
}
bool checkButtons() {
if (FreeRun_flag) {
if (!getDNDstatus()) {
FreeRun();
return 1;
} else { // если во время режима «Не беспокоить» нажата кнопка «Свободный запуск», запретить свободный запуск
Beep(3, 50, 50);
FreeRun_flag = false;
}
} else if (But_flag) {
Beep(1, 100, 0);
delay(1000); // Если кнопка удерживается нажатой
if (!digitalRead(ENCODER_BUTTON)) {
Settings();
return 1;
}
But_flag = false;
}
return 0;
}
void Beep(uint8_t rep, uint8_t dur_act, uint8_t dur_pass) { // повторение, длительность звукового сигнала, длительность пауз
for (int i = 0; i < rep; i++) {
digitalWrite(BUZZER, HIGH);
delay(dur_act);
digitalWrite(BUZZER, LOW);
if (i < (rep - 1)) delay(dur_pass);
}
}
void LoadPinsAndEEPROM() {
#define FanMotTrigEnabled_Address 0
#define waitDur_Address 1
#define Runtime_Address 3
#define DewTrigEnabled_Address 5
#define DewThres_Address 6
#define DewThresExtreme_Address 10
#define LightRelEnabled_Address 14
#define LDRenabled_Address 15
#define LDRThres_Address 16
#define DND_Enabled_Address 18 // Режим «Не беспокоить» включен
#define DND_SH_Address 19 // Час начала режима «Не беспокоить»
#define DND_SM_Address 20 // Минута начала режима DND
#define DND_FH_Address 21 // Час окончания режима DND
#define DND_FM_Address 22 // Минута завершения режима «Не беспокоить»
#define FreeRunDur_Address 23
EEPROM.get(FanMotTrigEnabled_Address, FanMotTrigEnabled);
EEPROM.get(waitDur_Address, waitDur);
EEPROM.get(Runtime_Address, Runtime);
EEPROM.get(DewTrigEnabled_Address, DewTrigEnabled);
EEPROM.get(DewThres_Address, DewThres);
EEPROM.get(DewThresExtreme_Address, DewThresExtreme);
EEPROM.get(LightRelEnabled_Address, LightRelEnabled);
EEPROM.get(LDRenabled_Address, LDRenabled);
EEPROM.get(LDRThres_Address, LDRThres);
EEPROM.get(DND_Enabled_Address, DNDenabled);
EEPROM.get(DND_SH_Address, DND_SH);
EEPROM.get(DND_SM_Address, DND_SM);
EEPROM.get(DND_FH_Address, DND_FH);
EEPROM.get(DND_FM_Address, DND_FM);
EEPROM.get(FreeRunDur_Address, FreeRunDur);
// ОТЛАДКА:
/*
FanMotTrigEnabled = 1;
waitDur = 10;
Runtime = 10;
DewTrigEnabled = true;
DewThres = 18.0;
DewThresExtreme = 19.0;
LightRelEnabled = true;
LDRenabled = true;
LDRThres = 512;
DNDenabled = false;
DND_SH = 23;
DND_SM = 45;
DND_FH = 0;
DND_FM = 2;
FreeRunDur = 30;
*/
pinMode(PIR, INPUT);
pinMode(LDR_READ, INPUT);
pinMode(ENC_A, INPUT);
pinMode(ENC_B, INPUT);
pinMode(CLOCK_INT, INPUT_PULLUP); // В таблице данных указано, что необходим подтягивающий резистор
pinMode(FREE_RUN_BUTTON, INPUT_PULLUP);
pinMode(ENCODER_BUTTON, INPUT_PULLUP);
pinMode(FAN_MAIN_REL, OUTPUT);
pinMode(FAN_SECOND_REL, OUTPUT);
pinMode(LIGHT_REL, OUTPUT);
pinMode(BUZZER, OUTPUT);
pinMode(LDR_PWR, OUTPUT);
}
void ISR_PIR() {
PIR_flag = true;
}
void ISR_ENCODER_BUTTON() {
But_flag = true;
}
ISR(PCINT1_vect) {
if (!digitalRead(FREE_RUN_BUTTON)) FreeRun_flag = true; // Кнопка имеет подтягивающий резистор, поэтому "!" используется.
}
ISR(TIMER1_COMPA_vect) {
TIM1_flag = true;
counter -= 5;
if (counter <= 0) swCounter(0);
}
// Ужасные коды настроек xd:
ISR(PCINT2_vect) { // Прерывание поворотного энкодера
static bool aLastState;
bool aState = digitalRead(ENC_A);
bool bState = digitalRead(ENC_B);
if (aState != aLastState) {
if (bState != aState) {
encVal++;
} else {
encVal--;
}
aLastState = aState;
}
}
void ButDebounce(bool cmd1) { // 0: Но кодер, 1: Свободный запуск
if (cmd1) {
while (!digitalRead(FREE_RUN_BUTTON)) delay(100); // Подождем, пока кнопка не будет отпущена
delay(200);
FreeRun_flag = false;
} else {
while (!digitalRead(ENCODER_BUTTON)) delay(100); // Подождем, пока кнопка не будет отпущена
delay(200);
But_flag = false;
}
}
String formatTime(int input, bool type = true) { // type = true означает, что ввод равен секундам, если false, это означает, что ввод равен минутам
int hours = input / 3600; // Получаем количество часов
int minutes = (input % 3600) / 60; // Получаем количество минут
int remainingSeconds = input % 60; // Получаем количество секунд
if (!type) {
hours = minutes;
minutes = remainingSeconds;
remainingSeconds = 0;
}
// Создаем отформатированную строку в формате "час:минута:секунда"
String formattedTime = "";
if (hours < 10) {
formattedTime += "0"; // Добавляем начальный ноль для часов
}
formattedTime += String(hours) + ":";
if (minutes < 10) {
formattedTime += "0"; // Добавляем начальный ноль для минут
}
formattedTime += String(minutes) + ":";
if (remainingSeconds < 10) {
formattedTime += "0"; // Добавляем начальный ноль для секунд
}
formattedTime += String(remainingSeconds);
return formattedTime;
}
uint8_t setEncVal(uint16_t target, uint8_t inc1, uint8_t inc2, uint16_t inc3, uint8_t lim1, uint8_t lim2) {
uint16_t a = 0;
uint8_t b = 0;
while (a < target) {
if (b < lim1) a += inc1;
else if (b < lim2) a += inc2;
else a += inc3;
b++;
}
return b;
}
void Settings() {} // удалено из-за ограничения на количество символов, но я все равно не запускаю эту функцию, пока она не зависла
@CaveScientist, 👍5
Обсуждение2 ответа
Это похоже на слишком амбициозные подтягивающие резисторы. Их должно быть около 3 тыс. на каждый автобус. Я возьму SWAG и скажу, что драйвер I2C потребляет большой ток и нагревается. Когда это происходит, его сопротивление увеличивается, тем самым обеспечивая меньший ток. Подберите правильные резисторы, и я уверен, что все будет работать нормально.
Если у вас есть замораживающий спрей, охладите их, и они, вероятно, начнут работать, пока не нагреются.
Если он оставался включенным в течение какого-то периода времени, возможно, вы повредили одну из микросхем, которая больше не управляет шиной должным образом. Если можно проверить это с помощью прицела. Если не спецчасти.
Это первый ответ, который показался мне разумным. На другом форуме мне сказали, что у меня проблемы с холодной пайкой или подключением, но это звучало нереально. Возможно, это объясняет, почему устройство работает дольше, когда я долго жду перед повторным включением, потому что полевые транзисторы остывают. Также это объясняет, почему в первый раз ему удалось проработать 2 дня, потому что полевые транзисторы I2C были исправны, но теперь они наполовину сломаны, поэтому это длится всего несколько часов. Это длится несколько секунд, когда я перезаряжаюсь, не дожидаясь. Я просто не могу поверить, что сопротивление в килоомах все еще может повредить полевые транзисторы. Я удалю некоторые резисторы из модулей., @CaveScientist
Я удалил DS3231, потому что на его плате было 4,7 кОм подтягиваний, поэтому общее сопротивление стало 5 кОм (по 10 кОм на SHT31 и OLED). К сожалению, через несколько минут устройство зависло., @CaveScientist
Вы уверены, что используете последнюю версию библиотеки I2C? Вплоть до середины 2020 года в «официальной» библиотеке I2C был известный режим сбоя, который приводил к зависанию шины I2C. См. мой пост «Дворец художников» на эту тему. тема для получения дополнительной информации.
Фрэнк
Есть комментарий: «Изменено Грейсоном Христофоро ([email protected]) в 2020 году для реализации тайм-аутов». Думаю, тайм-аут по умолчанию уже существует. Также я установил Arduino IDE 2 в апреле 2023 года. Что-то мне не хватает? Спасибо., @CaveScientist
- Wire.endTransmission() зависает
- Пайка несовмещенных выводов Arduino Pro Mini
- Несколько РАЗРЫВОВ I2C с подтягивающими резисторами не работают
- Случайные артефакты на OLED-экране SSD1306
- Сбой при записи данных MPU-6050 на SD-карту
- Проблема с настройкой i2c OLED wemos D1 mini shiled (64*24)
- Raspberry Pi, Teensy и Arduino на i2c
- Последовательный порт и I2C не работают вместе
Пробовали ли вы добавить специальные подтягивающие резисторы к SDA и SCL? Вроде 4,7 кОм. Это может помочь в данном случае, @chrisl
Нет, но у модулей есть подтяжки и это по 2,2к каждый, когда я замерял, @CaveScientist
первые три предложения описывают возможную проблему перегрева, @jsotola
В прошлый раз у меня были очень странные зависания I2C, это было вызвано сменой контакта ISR без его обработчика., @KIIV
У меня есть прерывание смены контакта, которое я использую, чтобы каждую минуту выводить Arduino из спящего режима с помощью Ds3231, и оно подключено к A3, а у меня есть кнопка, которая подключена к A2. Мне не нужен был обработчик для модуля часов, но поскольку они оба являются аналоговыми выводами, у меня есть «ISR(PCINT1_vect)» для кнопки, так что, думаю, я могу сказать, что у меня нет необработанных прерываний, даже несмотря на то, что A3 по выводам ничего не делает, кроме пробуждения., @CaveScientist
jsotola, нет нагрева ни регулятора, ни чипа. Вы говорите о МОП-транзисторах I2C внутри Arduino? Наверное, я не чувствую их пальцами. Думаю, для надежного теста мне нужно заменить Arduino, потому что МОП-транзисторы уже повреждены. Есть ли вероятность, что модуль температуры, или экран, или Ds3231 тоже сломан? Потому что, как я полагаю, они управляют линией SDA с помощью своих МОП-транзисторов., @CaveScientist
ОТ: Отсутствие разделения между сетью и низкоуровневой электроникой кажется мне опасным. Зазоры в К1 и К2 кажутся слишком маленькими. Соблюдали ли вы правила защиты людей?, @the busybee
Я не следовал никаким правилам, признаю это недостаток моей конструкции., @CaveScientist
Возможно, вы захотите изменить это перед вводом в эксплуатацию, чтобы не причинить вреда и не убить кого-либо, включая себя., @the busybee