Можно
Как по мне - это не всё, надо ещё учитывать событие отпускания кнопки, а этого нет в коде
Вообще то нужно схему выкладывать. Начинающие, при подключении кнопки, частенько ошибки делают
А если так? Только кнопку надо на землю законтачить и она срабатывает при отпускании.
Спойлер
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;
};
}
У меня работает, 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
Не нужно. Вообще при работе с кнопками нужно не текущее состояние проверять, а смену состояний. Т.е. события кнопки.
Но всё равно ведь, состояние “кнопка отпущена” надо учитывать?
Ведь , если этого не делать, то на одном нажатии можно все позиции пройти, и так по кругу.
Это не баг, а фича:)
Главное вовремя отпустить кнопку.
И вообще, ТС, удобнее управление так делать - нажал и ждёшь пока “отобразиться” нужный режим, отобразился-отпускай.
Нет. Вводится флаг oldState, равный текущему состоянию кнопки. И при каждом считывании состояние сравнивается с этим флагом. Если отличается - состояние изменилось - сохраняем oldState и делаем что нужно. А в какую сторону изменилось - уже не важно, как логику программы построишь, так и будет.
Зы: про дребезг контактов в курсе, для антидребезга нужно кой чего добавить, но принципа это не меняет.
Я бы сделал так: одиночный клик - включение или выключение, удерживание нажатой - при включенном фонаре изменение яркости, направление меняется при каждом удержании, удержание нажатой при выключенном фонарике - режим СОС, если уж без него никак. Двойной клик - режим стробоскопа. А то вымораживают эти китайские фонари - чтобы выключить, четыре раза кликнуть кнопкой нужно
Можно ещё так:
- если нет одиночного клика, то делаем то
- а если нет двойного клика, то делаем это
Сколько пользовался этими фонариками, разными, всегда мне было нужно только одно - включить, а потом выключить. Но хитрые китайцы так и вынуждают прощелкать все доступные режимы. Потому я за то, чтобы простым кликом только включать/выключать, а все свистоперделки - спрятать ))
был тут крутой фонарик на attiny85, сходу не нашёл
Перенести в мобильное приложение!
Простой код , три состояния яркости. Выкл - четвёртая позиция, или удержание в любой позиции.
Проверил в 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;
}
}
}
есть китайский драйвер, где есть возможность задавать режимы, на али его не нашел, купил на местном радиорынке, поменял заодно и светодиод и фонарь “заиграл новыми красками”
хм. на ардуино уно ваш код не работает так как описано в коде ((( а на симуляции работает. странно
вот я лол))) у вас схема подключения кнопки друга . все теперь работает также как и у вас)))
Ваше предложение очень интересное))) сам не раз сталкивался когда нужно выкл фонарик а он гад мигать начинает аж только потом выкл))))
крутяк. код почти так работает как я хочу)))) только вот при старте кода лед сразу светит а хочетцо чтобы включался только при нажатии кнопки) сейчас попробую сам что-то сделать))
а если еще при сработке режима фонарика SOS включать радиопередатчик с передачей радиосигнала SOS ?
а еще было бы неплохо GSM модуль присобачить и передавать местонахождения человека включившего режим SOS
ну это мои влажные мечты только