#1 2024-02-08 01:42:43

sxstalker
Учасник
Зареєстрований: 2024-02-08
Повідомлень: 26

Нужна помощь начинающему!!! 3-х режимный фонарик

Всем доброго времени суток!
Помогите пожалуйста начинающему в теме Arduino!
С помощью добрых людей создали каркас кода для 3-режимного фонарика. Но работает код как-то по рогульному как по мне :-)

Описание работы кода:
1. первое нажатие засвечивает лед на 100% яркость
2. второе нажатие засвечивает лед на 50% яркость
3. третье нажатие переводит лед в режим мигания SOS
4. четвертое нажатие отключает лед
5. хотелось бы зациклить выполнение нажатий по кругу

Какие проблемы у меня возникли на Arduino UNO:
1. нажимать кнопку нужно специфически немного удерживая чтобы все работало правильно. это ужасно бесит(( немного не так нажмешь и перепрыгивает сработка на второе или третье нажатие кнопки. добавил задержку но все равно это особо не помогает.
в китайском фонарике за 100 грн при кратком нажатии все срабатывает четко. хотелось бы повторить такую логику.
Я так понимаю нужно устранить дребезг кнопки но как прикрутить это все к этому коду пока не пойму)) Строго не судите я этой темой занялся может недели 2 назад.
2. И самое что мне не понятно. Для того чтобы выключить лед при четвертом нажатии иногда нужно нажимать кнопку несколько раз или вовсе ее удерживать. Всегда срабатывает по разному. очень редко отключается лед и при одном нажатии.

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

Помогите пожалуйста доработать код правильно. Это мне поможет двигаться далее.
Буду очень благодарен за вашу помощь! Пока не критикуйте я еще очень слаб в коде :-)
Если есть кто из вас кому не лень отвечать на мои вопросы желательно в телеграм то я буду очень вам признателен.
Также установил и Proteus 8 Pro если нужно могу там создать этот фонарик.
Также буду очень благодарен если подскажите на каком ресурсе бесплатном можно править код вдвоем онлайн?

Вот собственно сам код:

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

void setup() {
  pinMode(ledPin, OUTPUT);
  pinMode(buttonPin, INPUT);
}

void loop() {
  if (digitalRead(buttonPin) == HIGH) {
    delay(150); 
      mode = (mode + 1) % 4;

      switch (mode) { 
        case 0:
          analogWrite(ledPin, brightness); // а от тут зовсім не зрозумів чому analog, ну і встановлюэмо яскравість лед на 255
          break;
          case 1:
          analogWrite(ledPin, brightness / 2); // тут яскравість 255/2
          delay(150); 
          break;
        case 2:
          blinkSOS(); // а цей кейс запускає лед в режимі СОС
          break;
        case 3:
          analogWrite(ledPin, 0); 
          break;
      }
      }
}

void blinkSOS() {
   delay(50);
  // for (int i = 0; i <= 99999999; i++) { 
    while (1) { // --- создаем вечно замкнутый цикл
    delay(50);
          
    if (digitalRead(buttonPin) == HIGH) {
      goto bailout;
      // analogWrite(ledPin, 0); // яскравість на 0
    }
    else
    // от тут мені потрібні пояснення. знаю що i++ це аналог i=i+1. це цикл від 1 до 99999999999 але тут потрібно поставити нескінченність а я не знаю  як
    analogWrite(ledPin, brightness); // brightness яскравість на 255
    delay(300); // затримка 
    analogWrite(ledPin, 255 /3); // яскравість на 255/2
    delay(300);
    analogWrite(ledPin, 0); // яскравість на 0
    delay(300);
  }
  bailout:
  analogWrite(ledPin, 0); // яскравість на 0
   delay(1000);
}

Неактивний

#2 2024-02-08 13:02:51

dimich
Учасник
Зареєстрований: 2023-12-01
Повідомлень: 134

Re: Нужна помощь начинающему!!! 3-х режимный фонарик

sxstalker пише:

1. нажимать кнопку нужно специфически немного удерживая чтобы все работало правильно. это ужасно бесит((

У вашій програмі затримки по 150 мс і більше. В цей час процесор не опитує стан кнопки. Для придушення брязкоту контактів у більшості випадків достатньо кількох міллісекунд. А щоб не реагувати на утримання кнопки як на натиснення, потрібно перевіряти зміну стану кнопки з LOW в HIGH.

Для виміру проміжків часу асинхронно бібліотека ардуіно надає функцію millis(). Але нею теж потрібно правильно користуватись, щоб уникнути багів з переповненням.

sxstalker пише:
          analogWrite(ledPin, brightness); // а от тут зовсім не зрозумів чому analog, ну і встановлюэмо яскравість лед на 255

Яскравість регулюється широтно-імпульсною модуляцією. analogWrite() реалізує програмну ШІМ.

sxstalker пише:
  // for (int i = 0; i <= 99999999; i++) { 

Розмір int в AVR gcc - 16 біт, максимальне значення - 32767.

sxstalker пише:
    if (digitalRead(buttonPin) == HIGH) {
      goto bailout;

Для дострокового виходу з циклу достатньо break.

sxstalker пише:

Помогите пожалуйста доработать код правильно. Это мне поможет двигаться далее.

Спочатку реалізуйте просте увімкнення/вимкнення світлодіода по натисненню, щоб воно працювало стабільно. Потім уже додавайте режими, яскравість і т.д.

У вас два логічних потоки: обробка натиснення кнопки і виміри проміжків часу, тому це простіше реалізувати за допомогою скінченного автомата. Ваш switch(mode) / case по суті ним і є, тільки прибрати довгі delay(), а час вимірювати за допомогою millis().

І деякі загальні міркування.
Atmega328 сама по собі в активному режимі на частоті 16 МГц при живленні 5 В споживає близько 10 мА. У вашій програмі процесор постійно активний. У вимкненому стані при ємності джерела живлення в 2500 мА*год воно розрядиться за час порядка 250 годин.

Для мінімізації споживання у вимкненому стані процессор має бути в режимі PowerDown, а натиснення кнопки викликатиме зовнішне переривання. В увімкненому стані потрібно використовувати апаратну ШІМ, а процесор переводити в Idle. Також відключити всю непотрібну периферію, таку як ADC, а тактову частоту знизити. 16 МГц для обробки натиснення кнопки - це перебор.

Також цікаво, як ви збираєтесь реалізувати апаратну частину, особливо в плані живлення. Два Li-Ion аккумулятора? На платі ардуіно лінійний стабілізатор, при живленні 7.5 вольт ККД буде лише 66%. Якщо світлодіод яскравий, його потрібно живити напряму, і бажано через драйвер, щоб напруга живлення не впливала на яскравість.

Активний

#3 2024-02-08 14:29:33

г0cть
Гість

Re: Нужна помощь начинающему!!! 3-х режимный фонарик

https://wokwi.com/projects/389161053025161217

#4 2024-02-09 15:14:44

dimich
Учасник
Зареєстрований: 2023-12-01
Повідомлень: 134

Re: Нужна помощь начинающему!!! 3-х режимный фонарик

Якщо мета - розібратись як обробляти натискання кнопки і асинхронно блимати світлодіодом, не заморочуючись на оптимізації споживання, то пропоную такий варіант:

constexpr byte PIN_LED = 11;
constexpr byte PIN_BUTTON = 8;

constexpr byte DEBOUNCE_MS = 20;
constexpr byte DIM_BRIGHTNESS = 64;

constexpr unsigned int DIT_MS = 180;
constexpr unsigned int DAH_MS = DIT_MS * 3;
constexpr unsigned int WORD_SEP_MS = DIT_MS * 7;

const unsigned int sos_pattern[] = {
  DIT_MS, DIT_MS, DIT_MS, DIT_MS, DIT_MS, DIT_MS,
  DAH_MS, DIT_MS, DAH_MS, DIT_MS, DAH_MS, DIT_MS,
  DIT_MS, DIT_MS, DIT_MS, DIT_MS, DIT_MS, WORD_SEP_MS
};

void setup()
{
  pinMode(PIN_BUTTON, INPUT_PULLUP);
  pinMode(PIN_LED, OUTPUT);
}

static inline bool buttonPressed()
{
  return digitalRead(PIN_BUTTON) == LOW; // active low
}

static inline void ledOn(bool on)
{
  digitalWrite(PIN_LED, on); // active high
}

void loop()
{
  static struct {
    bool pressed { false };
    bool debounce { false };
    unsigned long last_press { 0 };
  } button;

  static struct state {
    enum mode {
      OFF = 0,
      ON,
      DIM,
      SOS,
      INVALID
    };
    mode active { mode::OFF };
    bool changed { true };
  } state;

  if (button.debounce) {
    if (millis() - button.last_press >= DEBOUNCE_MS) {
      button.debounce = false;
    }
  }

  bool button_state = buttonPressed();

  if ((button_state != button.pressed) && !button.debounce) {
    button.last_press = millis();
    button.debounce = true;
    button.pressed = button_state;

    if (button_state) {
      state.active = static_cast<state::mode>(state.active + 1);
      if (state.active >= state::mode::INVALID) {
        state.active = state::mode::OFF;
      }

      state.changed = true;
    }
  }

  static struct {
    unsigned long last_change;
    unsigned int pause;
    byte idx;
    bool led;
  } sos;

  if (state.changed) {
    switch (state.active) {
      case state::mode::OFF:
        ledOn(false);
        break;
      case state::mode::ON:
        ledOn(true);
        break;
      case state::mode::DIM:
        analogWrite(PIN_LED, DIM_BRIGHTNESS);
        break;
      case state::mode::SOS:
        sos.led = false;
        ledOn(sos.led);
        sos.idx = 0;
        sos.pause = 100;  // pause before start
        sos.last_change = millis();
        break;
      default: break;
    }
    state.changed = false;
  }

  if (state.active == state::mode::SOS) {
    unsigned long now = millis();
    if (now - sos.last_change >= sos.pause) {
      sos.led = !sos.led;
      ledOn(sos.led);
      sos.pause = sos_pattern[sos.idx++];
      if (sos.idx >= sizeof(sos_pattern) / sizeof(*sos_pattern)) {
        sos.idx = 0;
      }
      sos.last_change = now;
    }
  }
}

Тут кнопка active low, тобто пін підтягнутий до живлення внутрішнім pull-up і замикається кнопкою на землю.
Якщо у вас навпаки, пін 8 підтягнутий зовнішнім резистором до землі і замикається на живлення, замініть вміст buttonPressed() на

return digitalRead(PIN_BUTTON) == HIGH;

і в setup() INPUT_PULLUP замініть на INPUT:

  pinMode(PIN_BUTTON, INPUT);

Остання редакція dimich (2024-02-19 17:47:10)

Активний

#5 2024-02-09 18:26:04

sxstalker
Учасник
Зареєстрований: 2024-02-08
Повідомлень: 26

Re: Нужна помощь начинающему!!! 3-х режимный фонарик

dimich пише:
sxstalker пише:

1. нажимать кнопку нужно специфически немного удерживая чтобы все работало правильно. это ужасно бесит((

У вашій програмі затримки по 150 мс і більше. В цей час процесор не опитує стан кнопки. Для придушення брязкоту контактів у більшості випадків достатньо кількох міллісекунд. А щоб не реагувати на утримання кнопки як на натиснення, потрібно перевіряти зміну стану кнопки з LOW в HIGH.

Для виміру проміжків часу асинхронно бібліотека ардуіно надає функцію millis(). Але нею теж потрібно правильно користуватись, щоб уникнути багів з переповненням.

sxstalker пише:
          analogWrite(ledPin, brightness); // а от тут зовсім не зрозумів чому analog, ну і встановлюэмо яскравість лед на 255

Яскравість регулюється широтно-імпульсною модуляцією. analogWrite() реалізує програмну ШІМ.

sxstalker пише:
  // for (int i = 0; i <= 99999999; i++) { 

Розмір int в AVR gcc - 16 біт, максимальне значення - 32767.

sxstalker пише:
    if (digitalRead(buttonPin) == HIGH) {
      goto bailout;

Для дострокового виходу з циклу достатньо break.

sxstalker пише:

Помогите пожалуйста доработать код правильно. Это мне поможет двигаться далее.

Спочатку реалізуйте просте увімкнення/вимкнення світлодіода по натисненню, щоб воно працювало стабільно. Потім уже додавайте режими, яскравість і т.д.

У вас два логічних потоки: обробка натиснення кнопки і виміри проміжків часу, тому це простіше реалізувати за допомогою скінченного автомата. Ваш switch(mode) / case по суті ним і є, тільки прибрати довгі delay(), а час вимірювати за допомогою millis().

І деякі загальні міркування.
Atmega328 сама по собі в активному режимі на частоті 16 МГц при живленні 5 В споживає близько 10 мА. У вашій програмі процесор постійно активний. У вимкненому стані при ємності джерела живлення в 2500 мА*год воно розрядиться за час порядка 250 годин.

Для мінімізації споживання у вимкненому стані процессор має бути в режимі PowerDown, а натиснення кнопки викликатиме зовнішне переривання. В увімкненому стані потрібно використовувати апаратну ШІМ, а процесор переводити в Idle. Також відключити всю непотрібну периферію, таку як ADC, а тактову частоту знизити. 16 МГц для обробки натиснення кнопки - це перебор.

Також цікаво, як ви збираєтесь реалізувати апаратну частину, особливо в плані живлення. Два Li-Ion аккумулятора? На платі ардуіно лінійний стабілізатор, при живленні 7.5 вольт ККД буде лише 66%. Якщо світлодіод яскравий, його потрібно живити напряму, і бажано через драйвер, щоб напруга живлення не впливала на яскравість.

===
дуже дякую за вашу відповідь, поки що задача в мене розібратися з програмною частиною, робити фонарик самостійно на ардуіно якось зовсім не хочеться, тоді як їх повно недорогих ) хочеться на прикладі розібратися з кодом і  нарешті почати рухатися в плані створення моїх задумок, звичайні блималки на лед та просто вкл/викл я вже робив і вони всі працюють прекрасно а от тут стикнувся з халепою як для новачка )))

Неактивний

#6 2024-02-09 21:49:44

sxstalker
Учасник
Зареєстрований: 2024-02-08
Повідомлень: 26

Re: Нужна помощь начинающему!!! 3-х режимный фонарик

г0cть пише:

https://wokwi.com/projects/389161053025161217

===
Здрравствуйте! Вы оставили пустым

void blinkSOS() {

}
я решил добавить туда простое мигание лед имитацию SOS

void blinkSOS() {
analogWrite(ledPin, brightness);
delay(100);
analogWrite(ledPin, brightness /2);
delay(100);
analogWrite(ledPin, brightness /4);
delay(100);
analogWrite(ledPin, 0);
delay(100);
}
но теперь прервать мигание не могу :-) верне могу но только удержанием кнопки а хотелось бы обычным нажатием)
а вообще ваш код очень интересный и я его обязательно попробую разобрать на винтики  smile

Неактивний

#7 2024-02-09 21:58:41

sxstalker
Учасник
Зареєстрований: 2024-02-08
Повідомлень: 26

Re: Нужна помощь начинающему!!! 3-х режимный фонарик

dimich пише:

Якщо мета - розібратись як обробляти натискання кнопки і асинхронно блимати світлодіодом, не заморочуючись на оптимізації споживання, то пропоную такий варіант:

constexpr byte PIN_LED = 11;
constexpr byte PIN_BUTTON = 8;

constexpr byte DEBOUNCE_MS = 20;
constexpr byte DIM_BRIGHTNESS = 64;

constexpr unsigned int DIT_MS = 180;
constexpr unsigned int DAH_MS = DIT_MS * 3;
constexpr unsigned int WORD_SEP_MS = DIT_MS * 7;

const unsigned int sos_pattern[] = {
  DIT_MS, DIT_MS, DIT_MS, DIT_MS, DIT_MS, DIT_MS,
  DAH_MS, DIT_MS, DAH_MS, DIT_MS, DAH_MS, DIT_MS,
  DIT_MS, DIT_MS, DIT_MS, DIT_MS, DIT_MS, WORD_SEP_MS
};

void setup()
{
  pinMode(PIN_BUTTON, INPUT_PULLUP);
  pinMode(PIN_LED, OUTPUT);
}

static inline bool buttonPressed()
{
  return digitalRead(PIN_BUTTON) == LOW; // active low
}

static inline void ledOn(bool on)
{
  digitalWrite(PIN_LED, on); // active high
}

void loop()
{
  static struct {
    bool pressed { false };
    bool debounce { false };
    unsigned long last_press { 0 };
  } button;

  static struct state {
    enum mode {
      OFF = 0,
      ON,
      DIM,
      SOS,
      INVALID
    };
    mode active { mode::OFF };
    bool changed { true };
  } state;

  if (button.debounce) {
    if (millis() - button.last_press >= DEBOUNCE_MS) {
      button.debounce = false;
    }
  }

  bool button_state = buttonPressed();

  if ((button_state != button.pressed) && !button.debounce) {
    button.last_press = millis();
    button.debounce = true;
    button.pressed = button_state;

    if (button_state) {
      state.active = static_cast<state::mode>(state.active + 1);
      if (state.active >= state::mode::INVALID) {
        state.active = state::mode::OFF;
      }

      state.changed = true;
    }
  }

  static struct {
    unsigned long last_change;
    unsigned int pause;
    byte idx;
    bool led;
  } sos;

  if (state.changed) {
    switch (state.active) {
      case state::mode::OFF:
        ledOn(false);
        break;
      case state::mode::ON:
        ledOn(true);
        break;
      case state::mode::DIM:
        analogWrite(PIN_LED, DIM_BRIGHTNESS);
        break;
      case state::mode::SOS:
        sos.led = false;
        ledOn(sos.led);
        sos.idx = 0;
        sos.pause = 100;  // pause before start
        sos.last_change = millis();
        break;
      default: break;
    }
    state.changed = false;
  }

  if (state.active == state::mode::SOS) {
    unsigned long now = millis();
    if (now - sos.last_change >= sos.pause) {
      sos.led = !sos.led;
      ledOn(sos.led);
      sos.pause = sos_pattern[sos.idx++];
      if (sos.idx >= sizeof(sos_pattern) / sizeof(*sos_pattern)) {
        sos.idx = 0;
      }
      sos.last_change = now;
    }
  }
}

Тут кнопка active low, тобто пін підтягнутий до живлення внутрішнім pull-up і замикається кнопкою на землю.
Якщо у вас навпаки, пін 8 підтягнутий зовнішнім резистором до землі і замикається на живлення, замініть вміст buttonPressed() на

return digitalRead(PIN_BUTTON) == HIGH;

і в setup() INPUT_PULLUP замініть на INPUT:

  pinMode(PIN_BUTTON, INPUT);

===
Доброго дня! Ваш код на https://wokwi.com/projects/389279504810611713 повністью і бездоганно працює, потім ще спробую на реальній ардуінці, дуже вам дякую, шкода тільки що такий код для мене поки що заскладний, буду намагатися по операторам вивчати що воно таке, якщо буду задавати дурноваті питання не гнівайтеся на мене дуже будь-ласка  smile

Неактивний

#8 2024-02-09 22:42:08

г0cть
Гість

Re: Нужна помощь начинающему!!! 3-х режимный фонарик

sxstalker пише:

Здрравствуйте! Вы оставили пустым

void blinkSOS() {

}
я решил добавить туда простое мигание лед имитацию SOS

void blinkSOS() {
analogWrite(ledPin, brightness);
delay(100);
analogWrite(ledPin, brightness /2);
delay(100);
analogWrite(ledPin, brightness /4);
delay(100);
analogWrite(ledPin, 0);
delay(100);
}
но теперь прервать мигание не могу :-)

Гуглите блинк без delay

uint32_t t;
uint32_t period=500;

...


void blinkSOS() {
if(millis()-t>=period) {
t=millis();
digitalWrite(ledPin,!digitalRead(ledPin));
}
}

#9 2024-02-11 00:45:02

sxstalker
Учасник
Зареєстрований: 2024-02-08
Повідомлень: 26

Re: Нужна помощь начинающему!!! 3-х режимный фонарик

sxstalker пише:
dimich пише:

Якщо мета - розібратись як обробляти натискання кнопки і асинхронно блимати світлодіодом, не заморочуючись на оптимізації споживання, то пропоную такий варіант:

constexpr byte PIN_LED = 11;
constexpr byte PIN_BUTTON = 8;

constexpr byte DEBOUNCE_MS = 20;
constexpr byte DIM_BRIGHTNESS = 64;

constexpr unsigned int DIT_MS = 180;
constexpr unsigned int DAH_MS = DIT_MS * 3;
constexpr unsigned int WORD_SEP_MS = DIT_MS * 7;

const unsigned int sos_pattern[] = {
  DIT_MS, DIT_MS, DIT_MS, DIT_MS, DIT_MS, DIT_MS,
  DAH_MS, DIT_MS, DAH_MS, DIT_MS, DAH_MS, DIT_MS,
  DIT_MS, DIT_MS, DIT_MS, DIT_MS, DIT_MS, WORD_SEP_MS
};

void setup()
{
  pinMode(PIN_BUTTON, INPUT_PULLUP);
  pinMode(PIN_LED, OUTPUT);
}

static inline bool buttonPressed()
{
  return digitalRead(PIN_BUTTON) == LOW; // active low
}

static inline void ledOn(bool on)
{
  digitalWrite(PIN_LED, on); // active high
}

void loop()
{
  static struct {
    bool pressed { false };
    bool debounce { false };
    unsigned long last_press { 0 };
  } button;

  static struct state {
    enum mode {
      OFF = 0,
      ON,
      DIM,
      SOS,
      INVALID
    };
    mode active { mode::OFF };
    bool changed { true };
  } state;

  if (button.debounce) {
    if (millis() - button.last_press >= DEBOUNCE_MS) {
      button.debounce = false;
    }
  }

  bool button_state = buttonPressed();

  if ((button_state != button.pressed) && !button.debounce) {
    button.last_press = millis();
    button.debounce = true;
    button.pressed = button_state;

    if (button_state) {
      state.active = static_cast<state::mode>(state.active + 1);
      if (state.active >= state::mode::INVALID) {
        state.active = state::mode::OFF;
      }

      state.changed = true;
    }
  }

  static struct {
    unsigned long last_change;
    unsigned int pause;
    byte idx;
    bool led;
  } sos;

  if (state.changed) {
    switch (state.active) {
      case state::mode::OFF:
        ledOn(false);
        break;
      case state::mode::ON:
        ledOn(true);
        break;
      case state::mode::DIM:
        analogWrite(PIN_LED, DIM_BRIGHTNESS);
        break;
      case state::mode::SOS:
        sos.led = false;
        ledOn(sos.led);
        sos.idx = 0;
        sos.pause = 100;  // pause before start
        sos.last_change = millis();
        break;
      default: break;
    }
    state.changed = false;
  }

  if (state.active == state::mode::SOS) {
    unsigned long now = millis();
    if (now - sos.last_change >= sos.pause) {
      sos.led = !sos.led;
      ledOn(sos.led);
      sos.pause = sos_pattern[sos.idx++];
      if (sos.idx >= sizeof(sos_pattern) / sizeof(*sos_pattern)) {
        sos.idx = 0;
      }
      sos.last_change = now;
    }
  }
}

Тут кнопка active low, тобто пін підтягнутий до живлення внутрішнім pull-up і замикається кнопкою на землю.
Якщо у вас навпаки, пін 8 підтягнутий зовнішнім резистором до землі і замикається на живлення, замініть вміст buttonPressed() на

return digitalRead(PIN_BUTTON) == HIGH;

і в setup() INPUT_PULLUP замініть на INPUT:

  pinMode(PIN_BUTTON, INPUT);

===
Доброго дня! Ваш код на https://wokwi.com/projects/389279504810611713 повністью і бездоганно працює, потім ще спробую на реальній ардуінці, дуже вам дякую, шкода тільки що такий код для мене поки що заскладний, буду намагатися по операторам вивчати що воно таке, якщо буду задавати дурноваті питання не гнівайтеся на мене дуже будь-ласка  smile

===
перевірив. ваш код прекрасно працює !!! дуже дякую. але боюсь для мене заскладний як для новенького smile
може хтось з вас взявся би мене вчити бовдура?
наприклад даючи простеньке завдання
я в свою чергу буду намагатися його виконати на https://wokwi.com/

Неактивний

#10 2024-02-11 01:00:41

sxstalker
Учасник
Зареєстрований: 2024-02-08
Повідомлень: 26

Re: Нужна помощь начинающему!!! 3-х режимный фонарик

г0cть пише:

https://wokwi.com/projects/389161053025161217

===
ви мабудь видадили код з цієї сторінки?
я нажаль не можу більше на неї попасти

Неактивний

#11 2024-02-11 11:15:24

г0cть
Гість

Re: Нужна помощь начинающему!!! 3-х режимный фонарик

sxstalker пише:

ви мабудь видадили код з цієї сторінки?

Ні

#12 2024-02-13 22:15:37

sxstalker
Учасник
Зареєстрований: 2024-02-08
Повідомлень: 26

Re: Нужна помощь начинающему!!! 3-х режимный фонарик

г0cть пише:
sxstalker пише:

Здрравствуйте! Вы оставили пустым

void blinkSOS() {

}
я решил добавить туда простое мигание лед имитацию SOS

void blinkSOS() {
analogWrite(ledPin, brightness);
delay(100);
analogWrite(ledPin, brightness /2);
delay(100);
analogWrite(ledPin, brightness /4);
delay(100);
analogWrite(ledPin, 0);
delay(100);
}
но теперь прервать мигание не могу :-)

Гуглите блинк без delay

[code]

uint32_t t; // тут тоже не понимаю
uint32_t period=500; // тут мы присваиваем значение 500?

...


void blinkSOS() {
if(millis()-t>=period) {
t=millis();
digitalWrite(ledPin,!digitalRead(ledPin)); // тут мне совсем непонятно
}
}
[/code]

===
пожалуйста объясните мне эту часть кода если вам не сложно
---
---
можете набросать простой код мигающего светодиода без delay с такой последовательностью мигания
напрмиер:
светодиод горит 1 секунду, далее
светодиод не горит 2 секунды, далее
светодиод горит 0.5 секунды, далее
светодиод не горит 3 секунды
возможно так мне быстрее дойдет как работает мигалка без delay
смотрел на разных сайтах мигалки без delay но увы все с примером но без детального описания каждого оператора
---
может также посоветуете какой-то ресурс где разжовывают все очень подробно для новичков? буду вам очень благодарен

Неактивний

#13 2024-02-13 22:21:36

sxstalker
Учасник
Зареєстрований: 2024-02-08
Повідомлень: 26

Re: Нужна помощь начинающему!!! 3-х режимный фонарик

г0cть пише:
sxstalker пише:

ви мабудь видадили код з цієї сторінки?

Ні

===
ваша ссылка работает, простите, я лол запутался немного  smile

Неактивний

#14 2024-02-14 12:03:48

г0cть
Гість

Re: Нужна помощь начинающему!!! 3-х режимный фонарик

sxstalker пише:

пожалуйста объясните мне эту часть кода если вам не сложно

Перепишу код немного иначе:

unsigned long t; // создаем переменную в которую будем сохранять время наступления события. Тип переменной unsigned long
unsigned long period=500; // создаем переменную и присваиваем ей нужное значение периода с каким будет выполняться заданное событие

...
void setup() {
...
t=millis(); //запоминаем время
...
}

void blinkSOS() {
if(millis()-t>=period) { проверяем прошел ли заданный интервал времени с момента последнего сохранения значения t, если да то: 
t=millis(); //запоминаем новое значение времени
bool status_led=digitalRead(ledPin); //добавим еще одну переменную которая будет определять состояние светодиода - включен или выключен
digitalWrite(ledPin,!status_led); // инвертируем значение состояния светодиода на противоположное, и выводим новое значение. Т.е. если светодиод был включен, то выключаем и , соответственно, наоборот
}
//если нет - то продолжаем делать бутерброды, но при этом следим за временем :D 
}

millis() - ф-ция которая возвращает текущее значение времени прошедшее с момента старта кода (включения МК)
Ну, как-то так ...  roll

#15 2024-02-18 18:12:58

sxstalker
Учасник
Зареєстрований: 2024-02-08
Повідомлень: 26

Re: Нужна помощь начинающему!!! 3-х режимный фонарик

Блин, застрял я с этим millis (), запутался окончательно :-(
Казалось бы вроде, несложный должен быть код сигнала SOS, три точки, три тире, три точки, пауза, повтор предыдущего, короче цикл. Но у меня уже просто опустились руки, пересмотрел кучу роликов и сайтов, везде все описано бегло, как будо-то объясняют не для чайников а себе :-)
Отзовитесь у кого есть терпение объяснить и научить меня неумного мигать светодиодом без delay ()
Задача: научится мигать светодиодом с далеко не одним заданным интервалом. Большинство примеров что я видел с одним заданным параметром, тоесть светит и не светит  одинаковое время.
Я хочу попробовать понять на простом примере
Led светит 1 секунду
Led не светит 0.5 секунды
Повторить две команды 3 раза
Пауза номер 1 длительностью 5 секунд
Led светит 2 секунды
Led не светит 5 секунд
Повторить две команды 2 раза
Пауза номер 2  длительностью 10 секунд
Led светит 0.5 секунды
Led не светит 1.5 секунды
Led светит 1 секунду
Led не светит 2 секунды
Без повтора
---
Можем начать конечно с какого-то простого примера
Готов заплатить за помощь
Мне не нужно само решение, нужно научить меня самому понимать что я делаю :-)
Готов на любую форму обучения, режим связи с демонстрацией экрана по Скайп или текстовое общение, как вам будет удобно
Задавать могу много тупых вопросов :-)
Вроде немного но понимаю и иногда использую html. css а тут совсем присел от беспомощности двигаться дальше
Помогите пожалуйста разобраться
Розумію та спілкуюся також і Українською мовою звісно ж, але  в програмуванні можу тупити бо раніше все ж таки вивчав не по Українськи, так що вибачте якщо когось дратує моя мова!

Неактивний

#16 2024-02-18 20:34:07

Honey
Учасник
З Київ
Зареєстрований: 2020-09-26
Повідомлень: 414

Re: Нужна помощь начинающему!!! 3-х режимный фонарик

#define LED_PIN 13
#define LED_ON  HIGH
#define LED_OFF LOW

short durations[] = {
// state 0
  1000, -1, // always OFF (loop)
// state 2 (SOS)
   100, // initial delay
  // loop start
   500, 500,  500, 500,  500, 1500, // ON/OFF durations
  1500, 500, 1500, 500, 1500, 1500,
   500, 500,  500, 500,  500, 2000,
   -18, // goto 18 steps back == loop start
// state 22
  0,
  1000, 500, 1000, 500, 1000, 500+5000,
  2000, 5000, 2000, 5000+10000,
  500, 1500, 1000, 2000,
  -14, // Чєрєз пять мінут наступаєт зіма
};
byte state = 22; // state = 2 for SOS
unsigned long start_time = 0;

void setup(void) {
  pinMode(LED_PIN, OUTPUT);
}

void loop(void) {
  digitalWrite(LED_PIN, (state & 1) ? LED_ON : LED_OFF);
  unsigned long cur_time = millis();
  if (cur_time - start_time >= durations[state]) {
    if (durations[++state] < 0)
      state += durations[state];
    start_time = cur_time;
  }
}

Неактивний

#17 2024-02-18 20:35:45

г0сть
Учасник
Зареєстрований: 2022-06-09
Повідомлень: 45

Re: Нужна помощь начинающему!!! 3-х режимный фонарик

sxstalker пише:

Я хочу попробовать понять на простом примере
Led светит 1 секунду
Led не светит 0.5 секунды

---
Можем начать конечно с какого-то простого примера

unsigned long t, period;
unsigned long period_on = 1000; //Led светит 1 секунду
unsigned long period_off = 500; //Led не светит 0.5 секунды
bool status = LOW;  //задаем начальное состояние светодиода - выключено
int ledPin = 11;

void setup() {
  pinMode(ledPin, OUTPUT);
  t = millis();
}

void blinkSOS() {
  if (status == LOW) period = period_off; //проверяем в коаком состоянии должен быть светодиод, если должен быть выключен то период = интервалу выключения
  else period = period_on;  // иначе период равен времени включенного состояния
 
  if (millis() - t >= period) { //если заданный интервал прошел
    t = millis(); //запоминаем новое время
    status = !status; //меняем состояние светодиода на противоположное
    digitalWrite(ledPin, status); //и выводим его на индикацию
  }
}


void loop() {
blinkSOS();
}

Неактивний

#18 2024-02-18 20:38:24

г0сть
Учасник
Зареєстрований: 2022-06-09
Повідомлень: 45

Re: Нужна помощь начинающему!!! 3-х режимный фонарик

Honey, людина просила простий приклад. Хоча ваш код і виконує його забаганку, але навряд чи він розбереться у ньому.

Неактивний

#19 2024-02-18 22:03:34

dimich
Учасник
Зареєстрований: 2023-12-01
Повідомлень: 134

Re: Нужна помощь начинающему!!! 3-х режимный фонарик

sxstalker пише:

мигать светодиодом без delay ()

Уявіть, що ви готуєте, наприклад, борщ. Поміж інших дій потрібно відварити мʼясо 40 хвилин.

Ви можете поставити варитись мʼясо і стояти з секундоміром біля каструлі, поки не пройде 40 хвилин. Звісно, інших дій в цей час ви виконувати не можете. Це аналог delay().

Ви можете запамʼятати час коли поставили мʼясо, і періодично дивитись на годинник, перевіряючи, чи не пройшло 40 хвилин. В цей час ви можете займатись іншими діями. Це аналог рішення з millis(). Звісно, чим частіше ви дивитиметесь на годинник, тим точніше витримаєте ці 40 хвилин.

Ще можна завести будильник на 40 хвилин і не відволікатись від інших дій взагалі, поки він не задзвонить. Це буде аналог використання переривання по таймеру. Але це зовсім інша історія.

Розглянем 2-й варіант.

sxstalker пише:

Я хочу попробовать понять на простом примере
Led светит 1 секунду
Led не светит 0.5 секунды
Повторить две команды 3 раза
Пауза номер 1 длительностью 5 секунд
Led светит 2 секунды
Led не светит 5 секунд
Повторить две команды 2 раза
Пауза номер 2  длительностью 10 секунд
Led светит 0.5 секунды
Led не светит 1.5 секунды
Led светит 1 секунду
Led не светит 2 секунды
Без повтора

Така послідовність - сама по собі програма. Вона складається з команд для виконання. Вам потрібно реалізувати інтерпретатор, який виконуватиме ці команди. Щоб не ускладнювати реалізацію, а обійтись лише одним видом команд, "повтори" можна розгорнути. Звісно, це неоптимально, але простіше для розуміння.

Щоб відрізняти цю програму для виконання інтерпретатором від програми для ардуіно, яка реалізує сам інтерпертатор, в коментарях пишу її великими літерами: ПРОГРАМА. Вона представлена звичайним масивом структур cmd.

constexpr byte LED = 3;

// структура однієї команди
struct cmd {
  unsigned int dur;  // тривалість, мілісекунд (від 0 до 65535)
  byte led;          // стан світлодіода (HIGH/LOW)
};

// внутнішній стан інтерпретатора
struct {
  unsigned int pc;      // індекс поточної команди
  unsigned int dur;     // тривалість, скільки команда має виконуватись
  unsigned long start;  // час початку виконання команди
} interpreter;

// ПРОГРАМА для виконання: масив команд
// перше значення - тривалість в мілісекундах, друге - стан світлодіода
const cmd program[] = {
  { 1000, HIGH  },  // Led светит 1 секунду
  {  500, LOW   },  // Led не светит 0.5 секунды
  { 1000, HIGH  },  // повторюєм ще раз
  {  500, LOW   },
  { 1000, HIGH  },  // другий раз
  {  500, LOW   },
  { 1000, HIGH  },  // і третій ще раз
  {  500, LOW   },

  { 5000, LOW   },  // Пауза номер 1 длительностью 5 секунд (світлодіод не світить)
  { 2000, HIGH  },  // Led светит 2 секунды
  { 5000, LOW   },  // Led не светит 5 секунд
  { 2000, HIGH  },  // повторюєм ще раз
  { 5000, LOW   },
  { 2000, HIGH  },  // і другий раз
  { 5000, LOW   },

  {  500, HIGH  },  // Led светит 0.5 секунды
  { 1500, LOW   },  // Led не светит 1.5 секунды
  { 1000, HIGH  },  // Led светит 1 секунду
  { 2000, LOW   }   // Led не светит 2 секунды
};

void setup()
{
  pinMode(LED, OUTPUT);

  // запамʼятовуєм поточний час, щоб виконання ПРОГРАМИ в loop() почалося відразу.
  interpreter.start = millis();
  // наступні два рядки необовʼязкові, тому що глобальні змінні
  // автоматично ініціалізуються нулями. додано для ясності.
  interpreter.pc = 0;   // Починаєм виконання з команди з 0-м індексом
  interpreter.dur = 0;  // Починаєм відразу без затримки
}

// допоміжний макрос для обчислення кількості елементів у масиві: 
// розмір всього масива в байтах поділити на розмір першого елемента в байтах
// (всі елементи мають однаковий розмір)
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(*(x)))

void loop()
{
  // значення interpreter.pc за межами масиву ПРОГРАМИ означатиме, що виконання
  // ПРОГРАМИ завершено і нічого робити не потрібно
  if (interpreter.pc < ARRAY_SIZE(program)) // якщо ПРОГРАМУ ще не виконано до кінця
  {
    unsigned long now = millis(); // поточне значення часу

    if (now - interpreter.start >= interpreter.dur) // якщо настав час виконувати команду
    {
      digitalWrite(LED, program[interpreter.pc].led);  // вмикаєм чи вимикаєм світлодіод відповідно до команди
      interpreter.dur = program[interpreter.pc].dur;   // запамʼятовуєм, скільки часу виконувати поточну команду
      interpreter.start = now; // запамʼятовуєм час початку виконання
      // встановлюєм індекс команди, яку виконати наступного разу
      // якщо це була остання команда, виконання ПРОГРАМИ завершується
      // і очікування завершення поточної команди не відбувається
      interpreter.pc = interpreter.pc + 1;
      // якщо ПРОГРАМУ потрібно виконувати нескінченно, тоді скинути індекс команди на початок:
      // if (interpreter.pc >= ARRAY_SIZE(program)) {
      //   interpreter.pc = 0;
      // }
      // і прибрати умову `if (interpreter.pc < ARRAY_SIZE(program))`
    }
  }

  // Тут можна виконувати інші дії, такі як опитування кнопок і т.п.
  // Час виконання цих дій впливатиме на точність тривалостей виконання команд.
  // Для точності в 1 мілісекунду при 16 МГц ці дії не повинні виконуватись довше ніж 16000 тактів процесора
}

Тут, щоб було зрозуміліше, я намагався не використовувати ні загальні оптимізації, ні специфічні для AVR.

Так як станів світлодіоду всього два, можна не задавати новий стан у кожній команді, а просто з кожною командою змінювати стан на протилежний. Такий підхід використано в попередньому варіанті.

Можна піти далі і реалізувати окрему команду інтерпетатора "повтор". Тоді можна уникнути дублювання команд для повтору, але інтерпретатор стане трохи складнішим.

Остання редакція dimich (2024-02-19 17:46:13)

Активний

#20 2024-02-18 22:59:53

г0cть
Гість

Re: Нужна помощь начинающему!!! 3-х режимный фонарик

dimich пише:

Тут, щоб було зрозуміліше, я намагався не використовувати ні загальні оптимізації, ні специфічні для AVR.

Ага, человеку который не может разобраться с миллис предлагаем классы, структуры, массивы ... Да, так код короче и красивше, но зачем нагружать так сразу новичка?

#21 2024-02-18 23:09:43

Honey
Учасник
З Київ
Зареєстрований: 2020-09-26
Повідомлень: 414

Re: Нужна помощь начинающему!!! 3-х режимный фонарик

г0сть пише:

Honey, людина просила простий приклад. Хоча ваш код і виконує його забаганку, але навряд чи він розбереться у ньому.

В шести рядках (куди ж вже простіший код?) має розібратися. Там та ж ідея "інтерпретатора", яку щойно докладно "для домогосподарок" розписав dimich. (dimich, респект!)

Неактивний

#22 2024-02-18 23:17:31

г0cть
Гість

Re: Нужна помощь начинающему!!! 3-х режимный фонарик

Honey пише:

має розібратися.

Подивимось

#23 2024-02-19 00:13:34

dimich
Учасник
Зареєстрований: 2023-12-01
Повідомлень: 134

Re: Нужна помощь начинающему!!! 3-х режимный фонарик

г0cть пише:

Ага, человеку который не может разобраться с миллис предлагаем классы, структуры, массивы ... Да, так код короче и красивше, но зачем нагружать так сразу новичка?

Тут структури використовуються як звичайні C-шні структури, а не класи. Масив і структура - базові конструкції мов. Це перше, з чим необхідно розібратись, якщо мета - навчитись програмувати. Потім уже думати про ардуіно-специфічні фішки.

Структурування якраз допомагає зрозуміти алгоритм, на відміну від нагромадження глобальних змінних і спагетті-коду, які нажаль часто зустрічаються в прикладах скетчів для ардуіно.

Активний

#24 2024-02-20 02:06:43

sxstalker
Учасник
Зареєстрований: 2024-02-08
Повідомлень: 26

Re: Нужна помощь начинающему!!! 3-х режимный фонарик

Всім дуже дякую за допомогу, обов'язково роздивлюсь усі варіанти та мабудь буду задавати ще питання :-)

Неактивний

#25 2024-03-19 00:00:46

sxstalker
Учасник
Зареєстрований: 2024-02-08
Повідомлень: 26

Re: Нужна помощь начинающему!!! 3-х режимный фонарик

і знову я із своїм світлодіодом))
дійсно всі приклади вище для мене поки що мегаскладні нажаль
---
підкинула код гарна людина начебто для мене зрозумілий
АЛЕ
якщо я хочу виставити час світіння наприклад 300 а час несвітіння 1000 то код перестає працювати

unsigned long last_time;

void setup() {
  pinMode(13, OUTPUT);
  digitalWrite(13, LOW);
}

void loop() {
 

   if (millis() - last_time >= 300) {
     digitalWrite(13, HIGH);
     last_time = millis();
    }
      
    if (millis() - last_time >= 1000) {
     digitalWrite(13, LOW);
     last_time = millis();
    }
     
} 

підкажіть будь-ласка чому він падлюка не працює? :-)

Неактивний

Швидке повідомлення

Введіть повідомлення і натисніть Надіслати

Підвал форуму