Помогите понять что не так с моим кодом 3-х режимного фонарика!

Можно

1 лайк

Как по мне - это не всё, надо ещё учитывать событие отпускания кнопки, а этого нет в коде

Вообще то нужно схему выкладывать. Начинающие, при подключении кнопки, частенько ошибки делают

2 лайка

А если так? Только кнопку надо на землю законтачить и она срабатывает при отпускании.

Спойлер
int ledPin = 11;
int buttonPin = 8; 
int brightness = 255;
int mode = 0;
unsigned long previousMillis = 0;

void setup() {
  pinMode(ledPin, OUTPUT);
  pinMode(buttonPin, INPUT_PULLUP);//Кнопка замыкается на землю
 
}

void loop() {
  if (digitalRead(buttonPin) == LOW) {
    delay(100);
    while (digitalRead(buttonPin) == LOW) {};//Кнопка срабатывает при отпускании
    delay(100); 
    mode = (mode + 1) % 4;
  }
  switch (mode) {
    case 0:
      analogWrite(ledPin, brightness);
      break;
    case 1:
      analogWrite(ledPin, brightness / 2);
      delay(150);
      break;
    case 2:
      blinkSOS();
      break;
    case 3:
      analogWrite(ledPin, 0);
      break;
  }
}

void blinkSOS() {
  int td[21] = {10, 150, 150, 150, 150, 150, 150, 450, 450, 150, 450, 150, 450, 150, 450, 150, 150, 150, 150, 150, 150};
  int dt[21] = {1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0};
  static int nn = 0;
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= td[nn]) {
    previousMillis = currentMillis;
    digitalWrite(ledPin, dt[nn]);
    nn++; if (nn > 20)nn = 0; 
  };
}
2 лайка

У меня работает, 3 режима, SOS сам прикрутишь

Спойлер
/*
 Name:		Fonarik.ino
 Created:	10.02.2024 4:54:55
 Author:	DtS
*/
// interface
#pragma region Interface
#include <arduino.h>

constexpr uint8_t PIN_LED			= 11;		
constexpr uint8_t PIN_BUTTON		= 8;

constexpr bool BTN_ACTIVE_LEVEL		= LOW;		// кнопка с пина к GND 

constexpr uint8_t MIN_PWM_VALUE		= 0x00;
constexpr uint8_t MAX_PWM_VALUE		= 0xFF;
constexpr uint8_t HALF_PWM_VALUE	= 0x80;

constexpr uint32_t LED_FLASH_INTERVAL = 250;	// интервал мигания, мс

enum class TAppState {Unknown, Off, Hi, Low, Flash, Error}; // состояния фонарика

TAppState  AppState = TAppState::Unknown;

#pragma endregion

// implementation
#pragma region Implementation

bool ReadDigitalPin(const uint8_t APin) {
	for (uint8_t i = 0; i < 16; ++i) {
		if (digitalRead(APin) != BTN_ACTIVE_LEVEL) return false;
		delay(1);
	}
	return true;
}

int SerialPutChar(char ch, FILE* f){
	return Serial.print(ch);
}

void SetLedValue(const uint8_t AValue) {
	analogWrite(PIN_LED, AValue);
}

void doLedFlash(void) {
	static uint32_t lastLedFlash = 0;
	static bool     lastLedState = false;

	uint32_t now = millis();
	if (now - lastLedFlash < LED_FLASH_INTERVAL) return;
	lastLedFlash = now;

	lastLedState = !lastLedState;

	if (lastLedState)
		SetLedValue(MAX_PWM_VALUE);
	else
		SetLedValue(MIN_PWM_VALUE);
}

void SetAppState(const TAppState ANewState) {
	if (AppState == ANewState) return;

	AppState = ANewState;

	switch (AppState)
	{
	case TAppState::Unknown:
		SetAppState(TAppState::Error);
		break;
	case TAppState::Off:
		SetLedValue(MIN_PWM_VALUE);
		break;
	case TAppState::Hi:
		SetLedValue(MAX_PWM_VALUE);
		break;
	case TAppState::Low:
		SetLedValue(HALF_PWM_VALUE);
		break;
	case TAppState::Flash:
		break;
	case TAppState::Error:
		SetLedValue(MIN_PWM_VALUE);
		cli();
		abort();
		break;
	default:
		break;
	}
}

void SetNextAppState() {
	switch (AppState)
	{
	case TAppState::Off:
		SetAppState(TAppState::Hi);
		break;
	case TAppState::Hi:
		SetAppState(TAppState::Low);
		break;
	case TAppState::Low:
		SetAppState(TAppState::Flash);
		break;
	case TAppState::Flash:
		SetAppState(TAppState::Off);
		break;
	default:
		SetAppState(TAppState::Error);
		break;
	}
}

void setup() {
	Serial.begin(115200);
	stdout = fdevopen(SerialPutChar, NULL);
	puts("Programm started.");

	pinMode(PIN_LED, OUTPUT);
	pinMode(PIN_BUTTON, BTN_ACTIVE_LEVEL ? INPUT : INPUT_PULLUP);

	SetAppState(TAppState::Off);
}

void loop() {
	static bool lastBtnState = false;

	bool state = ReadDigitalPin(PIN_BUTTON);
	if (lastBtnState != state) {
		lastBtnState = state;
		if (!lastBtnState) SetNextAppState();
	}

	if (AppState == TAppState::Flash) doLedFlash();
  
}

#pragma endregion
1 лайк

Не нужно. Вообще при работе с кнопками нужно не текущее состояние проверять, а смену состояний. Т.е. события кнопки.

1 лайк

Но всё равно ведь, состояние “кнопка отпущена” надо учитывать?
Ведь , если этого не делать, то на одном нажатии можно все позиции пройти, и так по кругу.

Это не баг, а фича:)
Главное вовремя отпустить кнопку.
И вообще, ТС, удобнее управление так делать - нажал и ждёшь пока “отобразиться” нужный режим, отобразился-отпускай.

Нет. Вводится флаг oldState, равный текущему состоянию кнопки. И при каждом считывании состояние сравнивается с этим флагом. Если отличается - состояние изменилось - сохраняем oldState и делаем что нужно. А в какую сторону изменилось - уже не важно, как логику программы построишь, так и будет.

Зы: про дребезг контактов в курсе, для антидребезга нужно кой чего добавить, но принципа это не меняет.

1 лайк

Я бы сделал так: одиночный клик - включение или выключение, удерживание нажатой - при включенном фонаре изменение яркости, направление меняется при каждом удержании, удержание нажатой при выключенном фонарике - режим СОС, если уж без него никак. Двойной клик - режим стробоскопа. А то вымораживают эти китайские фонари - чтобы выключить, четыре раза кликнуть кнопкой нужно

Можно ещё так:

  • если нет одиночного клика, то делаем то
  • а если нет двойного клика, то делаем это

:slight_smile:

1 лайк

Сколько пользовался этими фонариками, разными, всегда мне было нужно только одно - включить, а потом выключить. Но хитрые китайцы так и вынуждают прощелкать все доступные режимы. Потому я за то, чтобы простым кликом только включать/выключать, а все свистоперделки - спрятать ))

1 лайк

был тут крутой фонарик на attiny85, сходу не нашёл

Перенести в мобильное приложение!

1 лайк

Простой код , три состояния яркости. Выкл - четвёртая позиция, или удержание в любой позиции.
Проверил в WOKWI. Не так давно открыл для себя этот ресурс, играюсь))

Спойлер

int ledPin = 11;
int buttonPin = 8;
int brightness = 255;
int mode = 0;

void setup() {
  pinMode(ledPin, OUTPUT);
  pinMode(buttonPin, INPUT_PULLUP);
}//кнопка подключена не как в первом скетче(#1)!!!


void loop() {

  static bool btnON = false;
  static unsigned long last_millis = 0;
  static unsigned int tik = 0;

  if (millis() - last_millis > 10) {
    last_millis = millis();

    if (!digitalRead(buttonPin)) {
      tik++;
      if (tik > 300) { //удержание более 3 сек -выкл из любой позиции
        btnON = false;
        mode = 3;
      }

      if (!btnON) {
        if (mode > 3)
          mode = 0;

        switch (mode) {
          case 0:
            analogWrite(ledPin, brightness);
            break;
          case 1:
            analogWrite(ledPin, brightness / 2);
            break;
          case 2:
            analogWrite(ledPin, brightness / 5);
            break;
          case 3:
            analogWrite(ledPin, 0);
            break;
        }
        mode++;
        btnON = true;
      }
    }
    else {
      btnON = false;
      tik = 0;
    }
  }
}
1 лайк

есть китайский драйвер, где есть возможность задавать режимы, на али его не нашел, купил на местном радиорынке, поменял заодно и светодиод и фонарь “заиграл новыми красками”

хм. на ардуино уно ваш код не работает так как описано в коде ((( а на симуляции работает. странно
вот я лол))) у вас схема подключения кнопки друга . все теперь работает также как и у вас)))

Ваше предложение очень интересное))) сам не раз сталкивался когда нужно выкл фонарик а он гад мигать начинает аж только потом выкл))))

крутяк. код почти так работает как я хочу)))) только вот при старте кода лед сразу светит а хочетцо чтобы включался только при нажатии кнопки) сейчас попробую сам что-то сделать))

а если еще при сработке режима фонарика SOS включать радиопередатчик с передачей радиосигнала SOS ?
а еще было бы неплохо GSM модуль присобачить и передавать местонахождения человека включившего режим SOS
ну это мои влажные мечты только :smiley: