Compare commits
No commits in common. "master" and "main" have entirely different histories.
|
@ -0,0 +1,34 @@
|
||||||
|
# ---> C++
|
||||||
|
# Prerequisites
|
||||||
|
*.d
|
||||||
|
|
||||||
|
# Compiled Object files
|
||||||
|
*.slo
|
||||||
|
*.lo
|
||||||
|
*.o
|
||||||
|
*.obj
|
||||||
|
|
||||||
|
# Precompiled Headers
|
||||||
|
*.gch
|
||||||
|
*.pch
|
||||||
|
|
||||||
|
# Compiled Dynamic libraries
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
*.dll
|
||||||
|
|
||||||
|
# Fortran module files
|
||||||
|
*.mod
|
||||||
|
*.smod
|
||||||
|
|
||||||
|
# Compiled Static libraries
|
||||||
|
*.lai
|
||||||
|
*.la
|
||||||
|
*.a
|
||||||
|
*.lib
|
||||||
|
|
||||||
|
# Executables
|
||||||
|
*.exe
|
||||||
|
*.out
|
||||||
|
*.app
|
||||||
|
|
463
EncButton.h
463
EncButton.h
|
@ -1,463 +0,0 @@
|
||||||
/*
|
|
||||||
Ультра лёгкая и быстрая библиотека для энкодера, энкодера с кнопкой или просто кнопки
|
|
||||||
Документация:
|
|
||||||
GitHub: https://github.com/GyverLibs/EncButton
|
|
||||||
Возможности:
|
|
||||||
- Максимально быстрое чтение пинов для AVR (ATmega328/ATmega168, ATtiny85/ATtiny13)
|
|
||||||
- Оптимизированный вес
|
|
||||||
- Быстрые и лёгкие алгоритмы кнопки и энкодера
|
|
||||||
- Энкодер: поворот, нажатый поворот, быстрый поворот, счётчик
|
|
||||||
- Кнопка: антидребезг, клик, несколько кликов, счётчик кликов, удержание, режим step
|
|
||||||
- Подключение - только HIGH PULL!
|
|
||||||
- Опциональный режим callback (+22б SRAM на каждый экземпляр)
|
|
||||||
|
|
||||||
AlexGyver, alex@alexgyver.ru
|
|
||||||
https://alexgyver.ru/
|
|
||||||
MIT License
|
|
||||||
Опционально используется алгоритм из библиотеки // https://github.com/mathertel/RotaryEncoder
|
|
||||||
|
|
||||||
Версии:
|
|
||||||
v1.1 - пуллап отдельныи методом
|
|
||||||
v1.2 - можно передать конструктору параметр INPUT_PULLUP / INPUT(умолч)
|
|
||||||
v1.3 - виртуальное зажатие кнопки энкодера вынесено в отдельную функцию + мелкие улучшения
|
|
||||||
v1.4 - обработка нажатия и отпускания кнопки
|
|
||||||
v1.5 - добавлен виртуальный режим
|
|
||||||
v1.6 - оптимизация работы в прерывании
|
|
||||||
v1.6.1 - PULLUP по умолчанию
|
|
||||||
v1.7 - большая оптимизация памяти, переделан FastIO
|
|
||||||
v1.8 - индивидуальная настройка таймаута удержания кнопки (была общая на всех)
|
|
||||||
v1.8.1 - убран FastIO
|
|
||||||
v1.9 - добавлена отдельная отработка нажатого поворота и запрос направления
|
|
||||||
v1.10 - улучшил обработку released, облегчил вес в режиме callback и исправил баги
|
|
||||||
v1.11 - ещё больше всякой оптимизации + настройка уровня кнопки
|
|
||||||
v1.11.1 - совместимость Digispark
|
|
||||||
v1.12 - добавил более точный алгоритм энкодера EB_BETTER_ENC
|
|
||||||
v1.13 - добавлен экспериментальный EncButton2
|
|
||||||
v1.14 - добавлена releaseStep(). Отпускание кнопки внесено в дебаунс
|
|
||||||
v1.15 - добавлен setPins() для EncButton2
|
|
||||||
v1.16 - добавлен режим EB_HALFSTEP_ENC для полушаговых энкодеров
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _EncButton_h
|
|
||||||
#define _EncButton_h
|
|
||||||
|
|
||||||
// ========= НАСТРОЙКИ (можно передефайнить из скетча) ==========
|
|
||||||
#define _EB_FAST 30 // таймаут быстрого поворота
|
|
||||||
#define _EB_DEB 50 // дебаунс кнопки
|
|
||||||
#define _EB_HOLD 1000 // таймаут удержания кнопки
|
|
||||||
#define _EB_STEP 500 // период срабатывания степ
|
|
||||||
#define _EB_CLICK 400 // таймаут накликивания
|
|
||||||
//#define EB_BETTER_ENC // точный алгоритм отработки энкодера (можно задефайнить в скетче)
|
|
||||||
|
|
||||||
// =========== НЕ ТРОГАЙ ============
|
|
||||||
#include <Arduino.h>
|
|
||||||
|
|
||||||
#ifndef nullptr
|
|
||||||
#define nullptr NULL
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// флаг макро
|
|
||||||
#define _EB_setFlag(x) (flags |= 1 << x)
|
|
||||||
#define _EB_clrFlag(x) (flags &= ~(1 << x))
|
|
||||||
#define _EB_readFlag(x) ((flags >> x) & 1)
|
|
||||||
|
|
||||||
#ifndef EB_FAST
|
|
||||||
#define EB_FAST _EB_FAST
|
|
||||||
#endif
|
|
||||||
#ifndef EB_DEB
|
|
||||||
#define EB_DEB _EB_DEB
|
|
||||||
#endif
|
|
||||||
#ifndef EB_HOLD
|
|
||||||
#define EB_HOLD _EB_HOLD
|
|
||||||
#endif
|
|
||||||
#ifndef EB_STEP
|
|
||||||
#define EB_STEP _EB_STEP
|
|
||||||
#endif
|
|
||||||
#ifndef EB_CLICK
|
|
||||||
#define EB_CLICK _EB_CLICK
|
|
||||||
#endif
|
|
||||||
|
|
||||||
enum eb_callback {
|
|
||||||
TURN_HANDLER, // 0
|
|
||||||
LEFT_HANDLER, // 1
|
|
||||||
RIGHT_HANDLER, // 2
|
|
||||||
LEFT_H_HANDLER, // 3
|
|
||||||
RIGHT_H_HANDLER, // 4
|
|
||||||
CLICK_HANDLER, // 5
|
|
||||||
HOLDED_HANDLER, // 6
|
|
||||||
STEP_HANDLER, // 7
|
|
||||||
PRESS_HANDLER, // 8
|
|
||||||
CLICKS_HANDLER, // 9
|
|
||||||
RELEASE_HANDLER, // 10
|
|
||||||
HOLD_HANDLER, // 11
|
|
||||||
TURN_H_HANDLER, // 12
|
|
||||||
// clicks amount 13
|
|
||||||
};
|
|
||||||
|
|
||||||
// константы
|
|
||||||
#define EB_TICK 0
|
|
||||||
#define EB_CALLBACK 1
|
|
||||||
|
|
||||||
#define EB_NO_PIN 255
|
|
||||||
|
|
||||||
#define VIRT_ENC 254
|
|
||||||
#define VIRT_ENCBTN 253
|
|
||||||
#define VIRT_BTN 252
|
|
||||||
|
|
||||||
#ifdef EB_BETTER_ENC
|
|
||||||
static const int8_t _EB_DIR[] = {
|
|
||||||
0, -1, 1, 0,
|
|
||||||
1, 0, 0, -1,
|
|
||||||
-1, 0, 0, 1,
|
|
||||||
0, 1, -1, 0
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// ===================================== CLASS =====================================
|
|
||||||
template < uint8_t _EB_MODE, uint8_t _S1 = EB_NO_PIN, uint8_t _S2 = EB_NO_PIN, uint8_t _KEY = EB_NO_PIN >
|
|
||||||
class EncButton {
|
|
||||||
public:
|
|
||||||
// можно указать режим работы пина
|
|
||||||
EncButton(const uint8_t mode = INPUT_PULLUP) {
|
|
||||||
if (_S1 < 252 && mode == INPUT_PULLUP) pullUp();
|
|
||||||
setButtonLevel(LOW);
|
|
||||||
}
|
|
||||||
|
|
||||||
// подтянуть пины внутренней подтяжкой
|
|
||||||
void pullUp() {
|
|
||||||
if (_S1 < 252) { // реальное устройство
|
|
||||||
if (_S2 == EB_NO_PIN) { // обычная кнопка
|
|
||||||
pinMode(_S1, INPUT_PULLUP);
|
|
||||||
} else if (_KEY == EB_NO_PIN) { // энк без кнопки
|
|
||||||
pinMode(_S1, INPUT_PULLUP);
|
|
||||||
pinMode(_S2, INPUT_PULLUP);
|
|
||||||
} else { // энк с кнопкой
|
|
||||||
pinMode(_S1, INPUT_PULLUP);
|
|
||||||
pinMode(_S2, INPUT_PULLUP);
|
|
||||||
pinMode(_KEY, INPUT_PULLUP);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// установить таймаут удержания кнопки для isHold(), мс (до 30 000)
|
|
||||||
void setHoldTimeout(int tout) {
|
|
||||||
_holdT = tout >> 7;
|
|
||||||
}
|
|
||||||
|
|
||||||
// виртуально зажать кнопку энкодера
|
|
||||||
void holdEncButton(bool state) {
|
|
||||||
if (state) _EB_setFlag(8);
|
|
||||||
else _EB_clrFlag(8);
|
|
||||||
}
|
|
||||||
|
|
||||||
// уровень кнопки: LOW - кнопка подключает GND (умолч.), HIGH - кнопка подключает VCC
|
|
||||||
void setButtonLevel(bool level) {
|
|
||||||
if (level) _EB_clrFlag(11);
|
|
||||||
else _EB_setFlag(11);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ===================================== TICK =====================================
|
|
||||||
// тикер, вызывать как можно чаще
|
|
||||||
// вернёт отличное от нуля значение, если произошло какое то событие
|
|
||||||
uint8_t tick(uint8_t s1 = 0, uint8_t s2 = 0, uint8_t key = 0) {
|
|
||||||
tickISR(s1, s2, key);
|
|
||||||
checkCallback();
|
|
||||||
return EBState;
|
|
||||||
}
|
|
||||||
|
|
||||||
// тикер специально для прерывания, не проверяет коллбэки
|
|
||||||
uint8_t tickISR(uint8_t s1 = 0, uint8_t s2 = 0, uint8_t key = 0) {
|
|
||||||
if (!_isrFlag) {
|
|
||||||
_isrFlag = 1;
|
|
||||||
|
|
||||||
// обработка энка (компилятор вырежет блок если не используется)
|
|
||||||
// если объявлены два пина или выбран вирт. энкодер или энкодер с кнопкой
|
|
||||||
if ((_S1 < 252 && _S2 < 252) || _S1 == VIRT_ENC || _S1 == VIRT_ENCBTN) {
|
|
||||||
uint8_t state;
|
|
||||||
if (_S1 >= 252) state = s1 | (s2 << 1); // получаем код
|
|
||||||
else state = fastRead(_S1) | (fastRead(_S2) << 1); // получаем код
|
|
||||||
poolEnc(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
// обработка кнопки (компилятор вырежет блок если не используется)
|
|
||||||
// если S2 не указан (кнопка) или указан KEY или выбран вирт. энкодер с кнопкой или кнопка
|
|
||||||
if ((_S1 < 252 && _S2 == EB_NO_PIN) || _KEY != EB_NO_PIN || _S1 == VIRT_BTN || _S1 == VIRT_ENCBTN) {
|
|
||||||
if (_S1 < 252 && _S2 == EB_NO_PIN) _btnState = fastRead(_S1); // обычная кнопка
|
|
||||||
if (_KEY != EB_NO_PIN) _btnState = fastRead(_KEY); // энк с кнопкой
|
|
||||||
if (_S1 == VIRT_BTN) _btnState = s1; // вирт кнопка
|
|
||||||
if (_S1 == VIRT_ENCBTN) _btnState = key; // вирт энк с кнопкой
|
|
||||||
_btnState ^= _EB_readFlag(11); // инверсия кнопки
|
|
||||||
poolBtn();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_isrFlag = 0;
|
|
||||||
return EBState;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ===================================== CALLBACK =====================================
|
|
||||||
// проверить callback, чтобы не дёргать в прерывании
|
|
||||||
void checkCallback() {
|
|
||||||
if (_EB_MODE) {
|
|
||||||
if (turn()) exec(0);
|
|
||||||
if (turnH()) exec(12);
|
|
||||||
if (EBState > 0 && EBState <= 8) exec(EBState);
|
|
||||||
if (release()) exec(10);
|
|
||||||
if (hold()) exec(11);
|
|
||||||
if (checkFlag(6)) {
|
|
||||||
exec(9);
|
|
||||||
if (clicks == _amount) exec(13);
|
|
||||||
}
|
|
||||||
EBState = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// подключить обработчик
|
|
||||||
void attach(eb_callback type, void (*handler)()) {
|
|
||||||
_callback[type] = *handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
// отключить обработчик
|
|
||||||
void detach(eb_callback type) {
|
|
||||||
_callback[type] = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// подключить обработчик на количество кликов (может быть только один!)
|
|
||||||
void attachClicks(uint8_t amount, void (*handler)()) {
|
|
||||||
_amount = amount;
|
|
||||||
_callback[13] = *handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
// отключить обработчик на количество кликов
|
|
||||||
void detachClicks() {
|
|
||||||
_callback[13] = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ===================================== STATUS =====================================
|
|
||||||
uint8_t getState() { return EBState; } // получить статус
|
|
||||||
void resetState() { EBState = 0; } // сбросить статус
|
|
||||||
|
|
||||||
// ======================================= ENC =======================================
|
|
||||||
bool left() { return checkState(1); } // поворот влево
|
|
||||||
bool right() { return checkState(2); } // поворот вправо
|
|
||||||
bool leftH() { return checkState(3); } // поворот влево нажатый
|
|
||||||
bool rightH() { return checkState(4); } // поворот вправо нажатый
|
|
||||||
|
|
||||||
bool fast() { return _EB_readFlag(1); } // быстрый поворот
|
|
||||||
bool turn() { return checkFlag(0); } // энкодер повёрнут
|
|
||||||
bool turnH() { return checkFlag(9); } // энкодер повёрнут нажато
|
|
||||||
int8_t getDir() { return _dir; } // направление последнего поворота, 1 или -1
|
|
||||||
int16_t counter = 0; // счётчик энкодера
|
|
||||||
|
|
||||||
// ======================================= BTN =======================================
|
|
||||||
bool press() { return checkState(8); } // кнопка нажата
|
|
||||||
bool release() { return checkFlag(10); } // кнопка отпущена
|
|
||||||
bool click() { return checkState(5); } // клик по кнопке
|
|
||||||
bool held() { return checkState(6); } // кнопка удержана
|
|
||||||
bool hold() { return _EB_readFlag(4); } // кнопка удерживается
|
|
||||||
bool step() { return checkState(7); } // режим импульсного удержания
|
|
||||||
bool state() { return _btnState; } // статус кнопки
|
|
||||||
bool releaseStep() {return checkFlag(12);} // кнопка отпущена после импульсного удержания
|
|
||||||
|
|
||||||
uint8_t clicks = 0; // счётчик кликов
|
|
||||||
bool hasClicks(uint8_t num) { return (clicks == num && checkFlag(7)) ? 1 : 0; } // имеются клики
|
|
||||||
uint8_t hasClicks() { return checkFlag(6) ? clicks : 0; } // имеются клики
|
|
||||||
|
|
||||||
// ===================================================================================
|
|
||||||
// =================================== DEPRECATED ====================================
|
|
||||||
bool isStep() { return step(); }
|
|
||||||
bool isHold() { return hold(); }
|
|
||||||
bool isHolded() { return held(); }
|
|
||||||
bool isHeld() { return held(); }
|
|
||||||
bool isClick() { return click(); }
|
|
||||||
bool isRelease() { return release(); }
|
|
||||||
bool isPress() { return press(); }
|
|
||||||
bool isTurnH() { return turnH(); }
|
|
||||||
bool isTurn() { return turn(); }
|
|
||||||
bool isFast() { return fast(); }
|
|
||||||
bool isLeftH() { return leftH(); }
|
|
||||||
bool isRightH() { return rightH(); }
|
|
||||||
bool isLeft() { return left(); }
|
|
||||||
bool isRight() { return right(); }
|
|
||||||
|
|
||||||
// ===================================== PRIVATE =====================================
|
|
||||||
private:
|
|
||||||
bool fastRead(const uint8_t pin) {
|
|
||||||
#if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__)
|
|
||||||
if (pin < 8) return bitRead(PIND, pin);
|
|
||||||
else if (pin < 14) return bitRead(PINB, pin - 8);
|
|
||||||
else if (pin < 20) return bitRead(PINC, pin - 14);
|
|
||||||
#elif defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny13__)
|
|
||||||
return bitRead(PINB, pin);
|
|
||||||
#else
|
|
||||||
return digitalRead(pin);
|
|
||||||
#endif
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ===================================== POOL ENC =====================================
|
|
||||||
void poolEnc(uint8_t state) {
|
|
||||||
#ifdef EB_BETTER_ENC
|
|
||||||
if (_prev != state) {
|
|
||||||
_ecount += _EB_DIR[state | (_prev << 2)]; // сдвиг внутреннего счётчика
|
|
||||||
_prev = state;
|
|
||||||
#ifdef EB_HALFSTEP_ENC // полушаговый энкодер
|
|
||||||
// спасибо https://github.com/GyverLibs/EncButton/issues/10#issue-1092009489
|
|
||||||
if ((state == 0x3 || state == 0x0) && _ecount != 0) {
|
|
||||||
#else // полношаговый
|
|
||||||
if (state == 0x3 && _ecount != 0) { // защёлкнули позицию
|
|
||||||
#endif
|
|
||||||
EBState = (_ecount < 0) ? 1 : 2;
|
|
||||||
_ecount = 0;
|
|
||||||
if (_S2 == EB_NO_PIN || _KEY != EB_NO_PIN) { // энкодер с кнопкой
|
|
||||||
if (!_EB_readFlag(4) && (_btnState || _EB_readFlag(8))) EBState += 2; // если кнопка не "удерживается"
|
|
||||||
}
|
|
||||||
_dir = (EBState & 1) ? -1 : 1; // направление
|
|
||||||
counter += _dir; // счётчик
|
|
||||||
if (EBState <= 2) _EB_setFlag(0); // флаг поворота для юзера
|
|
||||||
else if (EBState <= 4) _EB_setFlag(9); // флаг нажатого поворота для юзера
|
|
||||||
if (millis() - _debTimer < EB_FAST) _EB_setFlag(1); // быстрый поворот
|
|
||||||
else _EB_clrFlag(1); // обычный поворот
|
|
||||||
_debTimer = millis();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
if (_encRST && state == 0b11) { // ресет и энк защёлкнул позицию
|
|
||||||
if (_S2 == EB_NO_PIN || _KEY != EB_NO_PIN) { // энкодер с кнопкой
|
|
||||||
if ((_prev == 1 || _prev == 2) && !_EB_readFlag(4)) { // если кнопка не "удерживается" и энкодер в позиции 1 или 2
|
|
||||||
EBState = _prev;
|
|
||||||
if (_btnState || _EB_readFlag(8)) EBState += 2;
|
|
||||||
}
|
|
||||||
} else { // просто энкодер
|
|
||||||
if (_prev == 1 || _prev == 2) EBState = _prev;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (EBState > 0) { // был поворот
|
|
||||||
_dir = (EBState & 1) ? -1 : 1; // направление
|
|
||||||
counter += _dir; // счётчик
|
|
||||||
if (EBState <= 2) _EB_setFlag(0); // флаг поворота для юзера
|
|
||||||
else if (EBState <= 4) _EB_setFlag(9); // флаг нажатого поворота для юзера
|
|
||||||
if (millis() - _debTimer < EB_FAST) _EB_setFlag(1); // быстрый поворот
|
|
||||||
else _EB_clrFlag(1); // обычный поворот
|
|
||||||
}
|
|
||||||
|
|
||||||
_encRST = 0;
|
|
||||||
_debTimer = millis();
|
|
||||||
}
|
|
||||||
if (state == 0b00) _encRST = 1;
|
|
||||||
_prev = state;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// ===================================== POOL BTN =====================================
|
|
||||||
void poolBtn() {
|
|
||||||
uint32_t thisMls = millis();
|
|
||||||
uint32_t debounce = thisMls - _debTimer;
|
|
||||||
if (_btnState) { // кнопка нажата
|
|
||||||
if (!_EB_readFlag(3)) { // и не была нажата ранее
|
|
||||||
if (debounce > EB_DEB) { // и прошел дебаунс
|
|
||||||
_EB_setFlag(3); // флаг кнопка была нажата
|
|
||||||
_debTimer = thisMls; // сброс таймаутов
|
|
||||||
EBState = 8; // кнопка нажата
|
|
||||||
}
|
|
||||||
if (debounce > EB_CLICK) { // кнопка нажата после EB_CLICK
|
|
||||||
clicks = 0; // сбросить счётчик и флаг кликов
|
|
||||||
flags &= ~0b0011000011100000; // clear 5 6 7 12 13 (клики)
|
|
||||||
}
|
|
||||||
} else { // кнопка уже была нажата
|
|
||||||
if (!_EB_readFlag(4)) { // и удержание ещё не зафиксировано
|
|
||||||
if (debounce < (_holdT << 7)) { // прошло меньше удержания
|
|
||||||
if (EBState != 0 && EBState != 8) _EB_setFlag(2); // но энкодер повёрнут! Запомнили
|
|
||||||
} else { // прошло больше времени удержания
|
|
||||||
if (!_EB_readFlag(2)) { // и энкодер не повёрнут
|
|
||||||
EBState = 6; // значит это удержание (сигнал)
|
|
||||||
_EB_setFlag(4); // запомнили что удерживается
|
|
||||||
_debTimer = thisMls; // сброс таймаута
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else { // удержание зафиксировано
|
|
||||||
if (debounce > EB_STEP) { // таймер степа
|
|
||||||
EBState = 7; // сигналим
|
|
||||||
_EB_setFlag(13); // зафиксирован режим step
|
|
||||||
_debTimer = thisMls; // сброс таймаута
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else { // кнопка не нажата
|
|
||||||
if (_EB_readFlag(3)) { // но была нажата
|
|
||||||
if (debounce > EB_DEB) {
|
|
||||||
if (!_EB_readFlag(4) && !_EB_readFlag(2)) { // энкодер не трогали и не удерживали - это клик
|
|
||||||
EBState = 5;
|
|
||||||
clicks++;
|
|
||||||
}
|
|
||||||
flags &= ~0b00011100; // clear 2 3 4
|
|
||||||
_debTimer = thisMls; // сброс таймаута
|
|
||||||
_EB_setFlag(10); // кнопка отпущена
|
|
||||||
if (checkFlag(13)) _EB_setFlag(12); // кнопка отпущена после step
|
|
||||||
}
|
|
||||||
} else if (clicks > 0 && debounce > EB_CLICK && !_EB_readFlag(5)) flags |= 0b11100000; // set 5 6 7 (клики)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ===================================== MISC =====================================
|
|
||||||
bool checkState(uint8_t val) {
|
|
||||||
if (EBState == val) {
|
|
||||||
EBState = 0;
|
|
||||||
return 1;
|
|
||||||
} return 0;
|
|
||||||
}
|
|
||||||
bool checkFlag(uint8_t val) {
|
|
||||||
if (_EB_readFlag(val)) {
|
|
||||||
_EB_clrFlag(val);
|
|
||||||
return 1;
|
|
||||||
} return 0;
|
|
||||||
}
|
|
||||||
void exec(uint8_t num) {
|
|
||||||
if (*_callback[num]) _callback[num]();
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t _prev : 2;
|
|
||||||
uint8_t EBState : 4;
|
|
||||||
bool _btnState : 1;
|
|
||||||
bool _encRST : 1;
|
|
||||||
bool _isrFlag : 1;
|
|
||||||
uint16_t flags = 0;
|
|
||||||
|
|
||||||
#ifdef EB_BETTER_ENC
|
|
||||||
int8_t _ecount = 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
uint32_t _debTimer = 0;
|
|
||||||
uint8_t _holdT = (EB_HOLD >> 7);
|
|
||||||
int8_t _dir = 0;
|
|
||||||
void (*_callback[_EB_MODE ? 14 : 0])() = {};
|
|
||||||
uint8_t _amount = 0;
|
|
||||||
|
|
||||||
// flags
|
|
||||||
// 0 - enc turn
|
|
||||||
// 1 - enc fast
|
|
||||||
// 2 - enc был поворот
|
|
||||||
// 3 - флаг кнопки
|
|
||||||
// 4 - hold
|
|
||||||
// 5 - clicks flag
|
|
||||||
// 6 - clicks get
|
|
||||||
// 7 - clicks get num
|
|
||||||
// 8 - enc button hold
|
|
||||||
// 9 - enc turn holded
|
|
||||||
// 10 - btn released
|
|
||||||
// 11 - btn level
|
|
||||||
// 12 - btn released after step
|
|
||||||
// 13 - step flag
|
|
||||||
|
|
||||||
// EBState
|
|
||||||
// 0 - idle
|
|
||||||
// 1 - left
|
|
||||||
// 2 - right
|
|
||||||
// 3 - leftH
|
|
||||||
// 4 - rightH
|
|
||||||
// 5 - click
|
|
||||||
// 6 - held
|
|
||||||
// 7 - step
|
|
||||||
// 8 - press
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
83
buildTime.h
83
buildTime.h
|
@ -1,83 +0,0 @@
|
||||||
/*
|
|
||||||
Парсинг и получение даты и времени компиляции из __DATE__ и __TIME__
|
|
||||||
Документация:
|
|
||||||
GitHub: https://github.com/GyverLibs/buildTime
|
|
||||||
Константы времени компиляции:
|
|
||||||
BUILD_YEAR - год
|
|
||||||
BUILD_MONTH - месяц
|
|
||||||
BUILD_DAY - день
|
|
||||||
BUILD_HOUR - час
|
|
||||||
BUILD_MIN - минута
|
|
||||||
BUILD_SEC - секунда
|
|
||||||
|
|
||||||
Исходник http://qaru.site/questions/186859/how-to-use-date-and-time-predefined-macros-in-as-two-integers-then-stringify
|
|
||||||
AlexGyver, alex@alexgyver.ru
|
|
||||||
https://alexgyver.ru/
|
|
||||||
MIT License
|
|
||||||
|
|
||||||
Версии:
|
|
||||||
v1.0 - релиз
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef buildTime_h
|
|
||||||
#define buildTime_h
|
|
||||||
// Example of __DATE__ string: "Jul 27 2012"
|
|
||||||
// 01234567890
|
|
||||||
|
|
||||||
#define BUILD_YEAR_CH0 (__DATE__[7]-'0')
|
|
||||||
#define BUILD_YEAR_CH1 (__DATE__[8]-'0')
|
|
||||||
#define BUILD_YEAR_CH2 (__DATE__[9]-'0')
|
|
||||||
#define BUILD_YEAR_CH3 (__DATE__[10]-'0')
|
|
||||||
#define BUILD_YEAR (BUILD_YEAR_CH0*1000+BUILD_YEAR_CH1*100 + BUILD_YEAR_CH2*10+BUILD_YEAR_CH3)
|
|
||||||
|
|
||||||
#define BUILD_MONTH_IS_JAN (__DATE__[0] == 'J' && __DATE__[1] == 'a' && __DATE__[2] == 'n')
|
|
||||||
#define BUILD_MONTH_IS_FEB (__DATE__[0] == 'F')
|
|
||||||
#define BUILD_MONTH_IS_MAR (__DATE__[0] == 'M' && __DATE__[1] == 'a' && __DATE__[2] == 'r')
|
|
||||||
#define BUILD_MONTH_IS_APR (__DATE__[0] == 'A' && __DATE__[1] == 'p')
|
|
||||||
#define BUILD_MONTH_IS_MAY (__DATE__[0] == 'M' && __DATE__[1] == 'a' && __DATE__[2] == 'y')
|
|
||||||
#define BUILD_MONTH_IS_JUN (__DATE__[0] == 'J' && __DATE__[1] == 'u' && __DATE__[2] == 'n')
|
|
||||||
#define BUILD_MONTH_IS_JUL (__DATE__[0] == 'J' && __DATE__[1] == 'u' && __DATE__[2] == 'l')
|
|
||||||
#define BUILD_MONTH_IS_AUG (__DATE__[0] == 'A' && __DATE__[1] == 'u')
|
|
||||||
#define BUILD_MONTH_IS_SEP (__DATE__[0] == 'S')
|
|
||||||
#define BUILD_MONTH_IS_OCT (__DATE__[0] == 'O')
|
|
||||||
#define BUILD_MONTH_IS_NOV (__DATE__[0] == 'N')
|
|
||||||
#define BUILD_MONTH_IS_DEC (__DATE__[0] == 'D')
|
|
||||||
|
|
||||||
#define BUILD_MONTH \
|
|
||||||
( \
|
|
||||||
(BUILD_MONTH_IS_JAN) ? 1 : \
|
|
||||||
(BUILD_MONTH_IS_FEB) ? 2 : \
|
|
||||||
(BUILD_MONTH_IS_MAR) ? 3 : \
|
|
||||||
(BUILD_MONTH_IS_APR) ? 4 : \
|
|
||||||
(BUILD_MONTH_IS_MAY) ? 5 : \
|
|
||||||
(BUILD_MONTH_IS_JUN) ? 6 : \
|
|
||||||
(BUILD_MONTH_IS_JUL) ? 7 : \
|
|
||||||
(BUILD_MONTH_IS_AUG) ? 8 : \
|
|
||||||
(BUILD_MONTH_IS_SEP) ? 9 : \
|
|
||||||
(BUILD_MONTH_IS_OCT) ? 10 : \
|
|
||||||
(BUILD_MONTH_IS_NOV) ? 11 : \
|
|
||||||
(BUILD_MONTH_IS_DEC) ? 12 : \
|
|
||||||
/* error default */ '?' \
|
|
||||||
)
|
|
||||||
|
|
||||||
#define BUILD_DAY_CH0 (((__DATE__[4] >= '0') ? (__DATE__[4]) : '0')-'0')
|
|
||||||
#define BUILD_DAY_CH1 (__DATE__[5]-'0')
|
|
||||||
#define BUILD_DAY (BUILD_DAY_CH0*10+BUILD_DAY_CH1)
|
|
||||||
|
|
||||||
// Example of __TIME__ string: "21:06:19"
|
|
||||||
// 01234567
|
|
||||||
|
|
||||||
#define BUILD_HOUR_CH0 (__TIME__[0]-'0')
|
|
||||||
#define BUILD_HOUR_CH1 (__TIME__[1]-'0')
|
|
||||||
#define BUILD_HOUR (BUILD_HOUR_CH0*10+BUILD_HOUR_CH1)
|
|
||||||
|
|
||||||
#define BUILD_MIN_CH0 (__TIME__[3]-'0')
|
|
||||||
#define BUILD_MIN_CH1 (__TIME__[4]-'0')
|
|
||||||
#define BUILD_MIN (BUILD_MIN_CH0*10+BUILD_MIN_CH1)
|
|
||||||
|
|
||||||
#define BUILD_SEC_CH0 (__TIME__[6]-'0')
|
|
||||||
#define BUILD_SEC_CH1 (__TIME__[7]-'0')
|
|
||||||
#define BUILD_SEC (BUILD_SEC_CH0*10+BUILD_SEC_CH1)
|
|
||||||
|
|
||||||
#endif
|
|
251
feeder_v2.1.ino
251
feeder_v2.1.ino
|
@ -1,251 +0,0 @@
|
||||||
/*
|
|
||||||
Скетч к проекту "Спутниковая Роботорговля"
|
|
||||||
bgp, 2023
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#define EE_RESET 15 // любое число 0-255. Измени, чтобы сбросить настройки и обновить время
|
|
||||||
#define FEED_SPEED 1500 // задержка между шагами мотора (мкс)
|
|
||||||
#define DIRPIN 2 // d2 направление
|
|
||||||
#define STEPPIN 3 // d3 движение
|
|
||||||
#define BTN_PIN 4 // кнопка выдачи
|
|
||||||
#define BUNKERPIN 5 // d5 датчик наличия корма
|
|
||||||
#define HANDPIN 6 // d6 датчик наличия руки
|
|
||||||
#define TERM_INHIB 8 // d8 посылать "отказ от обслуживания" -upd
|
|
||||||
#define TERM_COIN 7 // d7 считаем монетки - upd
|
|
||||||
#define SLEEP 9 // d9 пока не работаем - двигатель спит
|
|
||||||
#define RELAY 10 // d10 ключ для отключения терминала и двигателя в ночное время
|
|
||||||
#define BTN_PIN2 11 // кнопка настройки
|
|
||||||
|
|
||||||
|
|
||||||
#define STEPS_FRW 19 // шаги вперёд
|
|
||||||
#define STEPS_BKW 12 // шаги назад
|
|
||||||
|
|
||||||
|
|
||||||
// =========================================================
|
|
||||||
#include <EEPROM.h>
|
|
||||||
#include "microDS3231.h"
|
|
||||||
MicroDS3231 rtc;
|
|
||||||
#include "EncButton.h"
|
|
||||||
|
|
||||||
|
|
||||||
EncButton<EB_TICK, BTN_PIN> btn;
|
|
||||||
EncButton<EB_TICK, BTN_PIN2> btn2;
|
|
||||||
EncButton<EB_TICK, BUNKERPIN> term_inhib;
|
|
||||||
// EncButton<EB_TICK, TERM_COIN> term_coin_put;
|
|
||||||
int feedAmount = 100;
|
|
||||||
|
|
||||||
// https://robotchip.ru/podklyuchenie-kupyuropriemnika-cashcode-sm-k-arduino/
|
|
||||||
|
|
||||||
int valuePulse = 10; // Стоимость одного импульса
|
|
||||||
int minWidthPulse = 80; // Минимальная ширина одного импульса
|
|
||||||
int maxWidthPulse = 120; // Максимальная ширина одного импульса
|
|
||||||
int debounce = 4; // Защита от помех
|
|
||||||
int pulseCount = 0; // Сколько импульсов получено
|
|
||||||
int receivedRUB = 0; // Сумма
|
|
||||||
unsigned long pulseDuration; // Как давно был последний импульс
|
|
||||||
unsigned long pulseBegin = 0; // Начало импульса
|
|
||||||
unsigned long pulseEnd = 0; // Конец импульса
|
|
||||||
unsigned long curtime; // Время
|
|
||||||
int postPulsePause = 300; // Время ожидания, для завершения подсчета импульсов
|
|
||||||
int pulseState; // Состояние входа "0" или "1"
|
|
||||||
int lastState = 1; // Последние состояние входа "0" или "1"
|
|
||||||
|
|
||||||
|
|
||||||
int paymentsCount = 0;
|
|
||||||
|
|
||||||
int onTime = 6; // время включения
|
|
||||||
int offTime = 20; // время выключения
|
|
||||||
|
|
||||||
|
|
||||||
unsigned long sleepTime; //переменная для задания времени сна
|
|
||||||
|
|
||||||
|
|
||||||
// GND -- [ R2 ] -- A0 -- [ R1 ] -- VIN
|
|
||||||
#define VREF 4.25 // точное напряжение на пине 5V (в данном случае зависит от стабилизатора на плате Arduino)
|
|
||||||
#define DIV_R1 10025 // точное значение 10 кОм резистора
|
|
||||||
#define DIV_R2 3285 // точное значение 3.3 кОм резистора
|
|
||||||
|
|
||||||
|
|
||||||
void setup() {
|
|
||||||
rtc.begin();
|
|
||||||
if (EEPROM.read(0) != EE_RESET) { // первый запуск
|
|
||||||
EEPROM.write(0, EE_RESET);
|
|
||||||
EEPROM.put(1, feedAmount);
|
|
||||||
rtc.setTime(BUILD_SEC, BUILD_MIN, BUILD_HOUR, BUILD_DAY, BUILD_MONTH, BUILD_YEAR);
|
|
||||||
}
|
|
||||||
EEPROM.get(1, feedAmount);
|
|
||||||
pinMode(STEPPIN, OUTPUT); // пин движения
|
|
||||||
pinMode(DIRPIN, OUTPUT); // пин направления
|
|
||||||
pinMode(HANDPIN, INPUT); // пин ИК датчик руки
|
|
||||||
pinMode(BUNKERPIN, INPUT); // пин ИК датчик бункер
|
|
||||||
pinMode(LED_BUILTIN, OUTPUT); // встроенный LED
|
|
||||||
pinMode(SLEEP, OUTPUT); // пин для отключения двигателя
|
|
||||||
pinMode(TERM_COIN, INPUT); // прием сигнала от терминала
|
|
||||||
pinMode(RELAY, OUTPUT);
|
|
||||||
|
|
||||||
|
|
||||||
sleepTime = 60 * 60 * 1000;
|
|
||||||
//Debug
|
|
||||||
// Serial.begin(9600); // debug в serial
|
|
||||||
}
|
|
||||||
|
|
||||||
void loop() {
|
|
||||||
static uint32_t tmr = 0;
|
|
||||||
float voltage = (float)analogRead(0) * VREF * ((DIV_R1 + DIV_R2) / DIV_R2) / 1024;
|
|
||||||
if (millis() - tmr > 500) { // два раза в секунду
|
|
||||||
static byte prevMin = 0;
|
|
||||||
tmr = millis();
|
|
||||||
DateTime now = rtc.getTime();
|
|
||||||
// получаем время
|
|
||||||
// Serial.print(voltage); // debug
|
|
||||||
// Serial.println(" V"); // debug
|
|
||||||
// Serial.print(now.hour); // debug
|
|
||||||
// Serial.println(" h"); // debug
|
|
||||||
// Serial.print(rtc.getHours());
|
|
||||||
// Serial.print(":");
|
|
||||||
// Serial.print(rtc.getMinutes());
|
|
||||||
// Serial.print(":");
|
|
||||||
// Serial.print(rtc.getSeconds());
|
|
||||||
// Serial.print(" ");
|
|
||||||
// Serial.print(rtc.getDay());
|
|
||||||
// Serial.print(" ");
|
|
||||||
// Serial.print(rtc.getDate());
|
|
||||||
// Serial.print("/");
|
|
||||||
// Serial.print(rtc.getMonth());
|
|
||||||
// Serial.print("/");
|
|
||||||
// Serial.println(rtc.getYear());
|
|
||||||
if (voltage < 13.2) {
|
|
||||||
digitalWrite(RELAY, LOW);
|
|
||||||
delay(sleepTime);
|
|
||||||
}
|
|
||||||
if ((now.hour >= onTime && now.hour < offTime && voltage > 13.2)) { //&& voltage > 10.2
|
|
||||||
digitalWrite(RELAY, HIGH); // работаем если время рабочее И напряжение выше минимума
|
|
||||||
} else {
|
|
||||||
digitalWrite(RELAY, LOW);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// состояние датчика рук
|
|
||||||
int hand = digitalRead(HANDPIN); //читать инпут с "датчика рук"
|
|
||||||
digitalWrite(TERM_INHIB, term_inhib.isHold() == 0);
|
|
||||||
|
|
||||||
|
|
||||||
btn.tick();
|
|
||||||
btn2.tick();
|
|
||||||
term_inhib.tick();
|
|
||||||
|
|
||||||
pulseState = digitalRead(TERM_COIN); // Считываем значение с входа TERM_COIN
|
|
||||||
curtime = millis(); // Записываем значение миллисекунд с момента начала выполнения программы
|
|
||||||
|
|
||||||
if ((pulseState == 0) && (lastState == 1)) // Ждем начало импульса, логический "0"
|
|
||||||
{
|
|
||||||
pulseBegin = curtime; // Записываем значение милисикунд
|
|
||||||
lastState = 0; // Записываем значение "0" в переменную lastState
|
|
||||||
} else if ((pulseState == 1) && (lastState == 0)) // Ждем окончания импульса, логический "1"
|
|
||||||
{
|
|
||||||
pulseDuration = curtime - pulseBegin; // Расчет длительности импульса в миллисекунд
|
|
||||||
if (pulseDuration > debounce) // Защита от помех, если импульс был небольшой
|
|
||||||
{
|
|
||||||
lastState = 1; // Записываем значение "1" в переменную lastState
|
|
||||||
}
|
|
||||||
if ((pulseDuration > minWidthPulse) && (pulseDuration < maxWidthPulse)) // Проверяем ширину импульса
|
|
||||||
{
|
|
||||||
pulseEnd = curtime; // Сохранить значение милисикунд, окончания импульса
|
|
||||||
pulseCount++; // Инкремент счетчика импульсов
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ((pulseEnd > 0) && (curtime - pulseEnd > postPulsePause)) // Проверяем, поступают ли еще импульсы
|
|
||||||
{
|
|
||||||
receivedRUB += pulseCount * valuePulse; // Расчет суммы
|
|
||||||
// if (hand == LOW)
|
|
||||||
// {
|
|
||||||
if (receivedRUB > 0) {
|
|
||||||
paymentsCount++; // Надо описать логику, "принимаем платежи сколько угодно раз и кормим столько же, как только в кадре появятся руки"
|
|
||||||
Serial.print("Credit: "); // Вывести текст на последовательный порт //Debug
|
|
||||||
Serial.print(receivedRUB); // Вывести значение переменной на последовательный порт //Debug
|
|
||||||
Serial.println(" RUB"); // Вывести текст на последовательный порт //Debug
|
|
||||||
Serial.print("Payed: ");
|
|
||||||
Serial.print(paymentsCount);
|
|
||||||
Serial.println(" times");
|
|
||||||
pulseEnd = 0; // Обнуляем
|
|
||||||
pulseCount = 0; // Обнуляем
|
|
||||||
receivedRUB = 0; // Обнуляем
|
|
||||||
}
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
if (paymentsCount > 0) // Debug подставляем руки, жмем кнопку - получаем внеочередную кормежку
|
|
||||||
{
|
|
||||||
feed();
|
|
||||||
paymentsCount--;
|
|
||||||
Serial.print("Payed: ");
|
|
||||||
Serial.print(paymentsCount);
|
|
||||||
Serial.println(" times");
|
|
||||||
}
|
|
||||||
|
|
||||||
// if (hand == LOW) // Debug подставляем руки, жмем кнопку - получаем внеочередную кормежку
|
|
||||||
// {
|
|
||||||
// feedByButton();
|
|
||||||
// }
|
|
||||||
|
|
||||||
if (btn.click()) {
|
|
||||||
feed();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (btn2.hold()) // Задаем размер порции:
|
|
||||||
{
|
|
||||||
int newAmount = 0;
|
|
||||||
while (btn2.isHold()) // пока кнопка зажата
|
|
||||||
{
|
|
||||||
btn2.tick();
|
|
||||||
oneRev(); // крутим мотор
|
|
||||||
newAmount++; // считаем шаги
|
|
||||||
}
|
|
||||||
feedAmount = newAmount;
|
|
||||||
EEPROM.put(1, feedAmount); // записываем количество шагов в постоянную память
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
} // конец основного цикла
|
|
||||||
|
|
||||||
// выдача корма
|
|
||||||
void feed() {
|
|
||||||
for (int i = 0; i < feedAmount; i++) oneRev();
|
|
||||||
disableMotor();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// кормежка по кнопке
|
|
||||||
void feedByButton() {
|
|
||||||
if (btn.click()) {
|
|
||||||
feed();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// выключаем ток на мотор
|
|
||||||
void disableMotor() {
|
|
||||||
digitalWrite(SLEEP, LOW);
|
|
||||||
}
|
|
||||||
|
|
||||||
// вращение
|
|
||||||
void runMotor() {
|
|
||||||
digitalWrite(SLEEP, HIGH); //Включаем мотор
|
|
||||||
digitalWrite(STEPPIN, HIGH); // по часовой
|
|
||||||
delayMicroseconds(FEED_SPEED);
|
|
||||||
digitalWrite(STEPPIN, LOW); // против часовой
|
|
||||||
delayMicroseconds(FEED_SPEED);
|
|
||||||
}
|
|
||||||
|
|
||||||
// вращение двигателя в виброрежиме, для недопущения заклинивания
|
|
||||||
void oneRev() {
|
|
||||||
for (int i = 0; i < STEPS_BKW; i++) {
|
|
||||||
digitalWrite(DIRPIN, LOW);
|
|
||||||
runMotor();
|
|
||||||
}
|
|
||||||
for (int i = 0; i < STEPS_FRW; i++) {
|
|
||||||
digitalWrite(DIRPIN, HIGH);
|
|
||||||
runMotor();
|
|
||||||
}
|
|
||||||
}
|
|
229
microDS3231.cpp
229
microDS3231.cpp
|
@ -1,229 +0,0 @@
|
||||||
#include "microDS3231.h"
|
|
||||||
|
|
||||||
//static const uint8_t _ds_daysInMonth[] PROGMEM = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
|
||||||
static uint8_t _ds_DIM(uint8_t i) {
|
|
||||||
return (i < 7) ? ((i == 1) ? 28 : ((i & 1) ? 30 : 31)) : ((i & 1) ? 31 : 30);
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint16_t getWeekDay(uint16_t y, uint8_t m, uint8_t d) {
|
|
||||||
if (y >= 2000)
|
|
||||||
y -= 2000;
|
|
||||||
uint16_t days = d;
|
|
||||||
for (uint8_t i = 1; i < m; ++i)
|
|
||||||
//days += pgm_read_byte(_ds_daysInMonth + i - 1);
|
|
||||||
days += _ds_DIM(i - 1);
|
|
||||||
if (m > 2 && y % 4 == 0)
|
|
||||||
++days;
|
|
||||||
return (days + 365 * y + (y + 3) / 4 + 4) % 7 + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// поiхали
|
|
||||||
MicroDS3231::MicroDS3231(uint8_t addr) : _addr(addr) {
|
|
||||||
Wire.begin();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MicroDS3231::begin(void){
|
|
||||||
Wire.begin(); // Инит шины
|
|
||||||
Wire.beginTransmission(_addr); // Зовем DS3231 по адресу
|
|
||||||
return (!Wire.endTransmission()); // если никто не откликнулся - возвращаем false
|
|
||||||
}
|
|
||||||
|
|
||||||
void MicroDS3231::setTime(int8_t seconds, int8_t minutes, int8_t hours, int8_t date, int8_t month, int16_t year) {
|
|
||||||
// защиты от дурака
|
|
||||||
month = constrain(month, 1, 12);
|
|
||||||
//date = constrain(date, 0, pgm_read_byte(_ds_daysInMonth + month - 1));
|
|
||||||
date = constrain(date, 0, _ds_DIM(month - 1));
|
|
||||||
seconds = constrain(seconds, 0, 59);
|
|
||||||
minutes = constrain(minutes, 0, 59);
|
|
||||||
hours = constrain(hours, 0, 23);
|
|
||||||
|
|
||||||
// отправляем
|
|
||||||
uint8_t day = getWeekDay(year, month, date);
|
|
||||||
year -= 2000;
|
|
||||||
Wire.beginTransmission(_addr);
|
|
||||||
Wire.write(0x00);
|
|
||||||
Wire.write(encodeRegister(seconds));
|
|
||||||
Wire.write(encodeRegister(minutes));
|
|
||||||
if (hours > 19) Wire.write((0x2 << 4) | (hours % 20));
|
|
||||||
else if (hours > 9) Wire.write((0x1 << 4) | (hours % 10));
|
|
||||||
else Wire.write(hours);
|
|
||||||
Wire.write(day);
|
|
||||||
Wire.write(encodeRegister(date));
|
|
||||||
Wire.write(encodeRegister(month));
|
|
||||||
Wire.write(encodeRegister(year));
|
|
||||||
Wire.endTransmission();
|
|
||||||
}
|
|
||||||
|
|
||||||
void MicroDS3231::setHMSDMY(int8_t hours, int8_t minutes, int8_t seconds, int8_t date, int8_t month, int16_t year) {
|
|
||||||
setTime(seconds, minutes, hours, date, month, year);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MicroDS3231::setTime(DateTime time) {
|
|
||||||
setTime(time.second, time.minute, time.hour, time.date, time.month, time.year);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int charToDec(const char* p) {
|
|
||||||
return (10 * (*p - '0') + (*++p - '0'));
|
|
||||||
}
|
|
||||||
|
|
||||||
void MicroDS3231::setTime(const __FlashStringHelper* stamp) {
|
|
||||||
char buff[25];
|
|
||||||
memcpy_P(buff, stamp, 25);
|
|
||||||
|
|
||||||
// Wed Jul 14 22:00:24 2021
|
|
||||||
// 4 8 11 14 17 22
|
|
||||||
// Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
|
|
||||||
int h, m, s, d, mo, y;
|
|
||||||
h = charToDec(buff + 11);
|
|
||||||
m = charToDec(buff + 14);
|
|
||||||
s = charToDec(buff + 17);
|
|
||||||
d = charToDec(buff + 8);
|
|
||||||
switch (buff[4]) {
|
|
||||||
case 'J': mo = (buff[5] == 'a') ? 1 : (mo = (buff[6] == 'n') ? 6 : 7); break;
|
|
||||||
case 'F': mo = 2; break;
|
|
||||||
case 'A': mo = (buff[6] == 'r') ? 4 : 8; break;
|
|
||||||
case 'M': mo = (buff[6] == 'r') ? 3 : 5; break;
|
|
||||||
case 'S': mo = 9; break;
|
|
||||||
case 'O': mo = 10; break;
|
|
||||||
case 'N': mo = 11; break;
|
|
||||||
case 'D': mo = 12; break;
|
|
||||||
}
|
|
||||||
y = 2000 + charToDec(buff + 22);
|
|
||||||
setTime(s, m, h, d, mo, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
DateTime MicroDS3231::getTime() {
|
|
||||||
DateTime now;
|
|
||||||
Wire.beginTransmission(_addr);
|
|
||||||
Wire.write(0x0);
|
|
||||||
if (Wire.endTransmission() != 0) return now;
|
|
||||||
Wire.requestFrom(_addr, (uint8_t)7);
|
|
||||||
now.second = unpackRegister(Wire.read());
|
|
||||||
now.minute = unpackRegister(Wire.read());
|
|
||||||
now.hour = unpackHours(Wire.read());
|
|
||||||
now.day = Wire.read();
|
|
||||||
now.date = unpackRegister(Wire.read());
|
|
||||||
now.month = unpackRegister(Wire.read());
|
|
||||||
now.year = unpackRegister(Wire.read()) + 2000;
|
|
||||||
return now;
|
|
||||||
}
|
|
||||||
String MicroDS3231::getTimeString() {
|
|
||||||
DateTime now = getTime();
|
|
||||||
String str = "";
|
|
||||||
if (now.hour < 10) str += '0';
|
|
||||||
str += now.hour;
|
|
||||||
str += ':';
|
|
||||||
if (now.minute < 10) str += '0';
|
|
||||||
str += now.minute;
|
|
||||||
str += ':';
|
|
||||||
if (now.second < 10) str += '0';
|
|
||||||
str += now.second;
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
String MicroDS3231::getDateString() {
|
|
||||||
DateTime now = getTime();
|
|
||||||
String str = "";
|
|
||||||
if (now.date < 10) str += '0';
|
|
||||||
str += now.date;
|
|
||||||
str += '.';
|
|
||||||
if (now.month < 10) str += '0';
|
|
||||||
str += now.month;
|
|
||||||
str += '.';
|
|
||||||
str += now.year;
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
void MicroDS3231::getTimeChar(char* array) {
|
|
||||||
DateTime now = getTime();
|
|
||||||
array[0] = now.hour / 10 + '0';
|
|
||||||
array[1] = now.hour % 10 + '0';
|
|
||||||
array[2] = ':';
|
|
||||||
array[3] = now.minute / 10 + '0';
|
|
||||||
array[4] = now.minute % 10 + '0';
|
|
||||||
array[5] = ':';
|
|
||||||
array[6] = now.second / 10 + '0';
|
|
||||||
array[7] = now.second % 10 + '0';
|
|
||||||
array[8] = '\0';
|
|
||||||
}
|
|
||||||
void MicroDS3231::getDateChar(char* array) {
|
|
||||||
DateTime now = getTime();
|
|
||||||
array[0] = now.date / 10 + '0';
|
|
||||||
array[1] = now.date % 10 + '0';
|
|
||||||
array[2] = '.';
|
|
||||||
array[3] = now.month / 10 + '0';
|
|
||||||
array[4] = now.month % 10 + '0';
|
|
||||||
array[5] = '.';
|
|
||||||
itoa(now.year, array + 6, DEC);
|
|
||||||
array[10] = '\0';
|
|
||||||
}
|
|
||||||
bool MicroDS3231::lostPower(void) { // возвращает true, если 1 января 2000
|
|
||||||
if (getYear() == 2000 && getMonth() == 1 && getDate() == 1) return true;
|
|
||||||
else return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t MicroDS3231::getSeconds(void) {
|
|
||||||
return (unpackRegister(readRegister(0x00)));
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t MicroDS3231::getMinutes(void) {
|
|
||||||
return (unpackRegister(readRegister(0x01)));
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t MicroDS3231::getHours(void) {
|
|
||||||
return (unpackHours(readRegister(0x02)));
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t MicroDS3231::getDay(void) {
|
|
||||||
return readRegister(0x03);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t MicroDS3231::getDate(void) {
|
|
||||||
return (unpackRegister(readRegister(0x04)));
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t MicroDS3231::getMonth(void) {
|
|
||||||
return (unpackRegister(readRegister(0x05)));
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t MicroDS3231::getYear(void) {
|
|
||||||
return (unpackRegister(readRegister(0x06)) + 2000);
|
|
||||||
}
|
|
||||||
|
|
||||||
// сервис
|
|
||||||
uint8_t MicroDS3231::readRegister(uint8_t addr) {
|
|
||||||
Wire.beginTransmission(_addr);
|
|
||||||
Wire.write(addr);
|
|
||||||
if (Wire.endTransmission() != 0) return 0;
|
|
||||||
Wire.requestFrom(_addr, (uint8_t)1);
|
|
||||||
uint8_t data = Wire.read();
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t MicroDS3231::unpackRegister(uint8_t data) {
|
|
||||||
return ((data >> 4) * 10 + (data & 0xF));
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t MicroDS3231::encodeRegister(int8_t data) {
|
|
||||||
return (((data / 10) << 4) | (data % 10));
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t MicroDS3231::unpackHours(uint8_t data) {
|
|
||||||
if (data & 0x20) return ((data & 0xF) + 20);
|
|
||||||
else if (data & 0x10) return ((data & 0xF) + 10);
|
|
||||||
else return (data & 0xF);
|
|
||||||
}
|
|
||||||
|
|
||||||
float MicroDS3231::getTemperatureFloat(void) {
|
|
||||||
return (getTemperatureRaw() * 0.25f);
|
|
||||||
}
|
|
||||||
|
|
||||||
int MicroDS3231::getTemperature(void) {
|
|
||||||
return (getTemperatureRaw() >> 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
int MicroDS3231::getTemperatureRaw(void) {
|
|
||||||
Wire.beginTransmission(_addr);
|
|
||||||
Wire.write(0x11);
|
|
||||||
Wire.endTransmission();
|
|
||||||
Wire.requestFrom(_addr, (uint8_t)2);
|
|
||||||
return ((int8_t)Wire.read() << 2) + (Wire.read() >> 6);
|
|
||||||
}
|
|
|
@ -1,82 +0,0 @@
|
||||||
/*
|
|
||||||
Лёгкая библиотека для работы с RTC DS3231 для Arduino
|
|
||||||
Документация:
|
|
||||||
GitHub: https://github.com/GyverLibs/microDS3231
|
|
||||||
Возможности:
|
|
||||||
- Чтение и запись времени
|
|
||||||
- Вывод в char* и String
|
|
||||||
- Чтение температуры датчика
|
|
||||||
|
|
||||||
Egor 'Nich1con' Zakharov & AlexGyver, alex@alexgyver.ru
|
|
||||||
https://alexgyver.ru/
|
|
||||||
MIT License
|
|
||||||
|
|
||||||
Версии:
|
|
||||||
v1.2 - добавлены ограничения на вводимые в setTime числа. Также нельзя ввести 29 февраля увы =)
|
|
||||||
v1.3 - пофикшено зависание, когда модуль отключен но опрашивается
|
|
||||||
v1.4 - незначительный фикс
|
|
||||||
v2.0 - новые возможности, оптимизация и облегчение
|
|
||||||
v2.1 - добавил вывод температуры, вывод в String и char
|
|
||||||
v2.2 - исправлены дни недели (пн-вс 1-7)
|
|
||||||
v2.3 - небольшие исправления, оптимизация, изменён порядок вывода даты
|
|
||||||
v2.4 - исправлена установка времени компиляции
|
|
||||||
v2.5 - добавлен begin для проверки наличия модуля на линии
|
|
||||||
v2.6 - исправлены отрицательные температуры
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef microDS3231_h
|
|
||||||
#define microDS3231_h
|
|
||||||
//#include <microWire.h> // выбор между библиотеками Wire и microWire
|
|
||||||
#include <Wire.h>
|
|
||||||
#include "buildTime.h"
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
#define COMPILE_TIME F(__TIMESTAMP__)
|
|
||||||
|
|
||||||
struct DateTime {
|
|
||||||
uint8_t second;
|
|
||||||
uint8_t minute;
|
|
||||||
uint8_t hour;
|
|
||||||
uint8_t day;
|
|
||||||
uint8_t date;
|
|
||||||
uint8_t month;
|
|
||||||
uint16_t year;
|
|
||||||
};
|
|
||||||
|
|
||||||
class MicroDS3231 {
|
|
||||||
public:
|
|
||||||
MicroDS3231(uint8_t addr = 0x68); // конструктор. Можно передать адрес
|
|
||||||
bool begin(void); // инициализация, вернет true, если RTC найден
|
|
||||||
void setTime(const __FlashStringHelper* stamp); // установка времени == времени компиляции
|
|
||||||
void setTime(DateTime time); // установить из структуры DateTime
|
|
||||||
void setTime(int8_t seconds, int8_t minutes, int8_t hours, int8_t date, int8_t month, int16_t year); // установка времени
|
|
||||||
void setHMSDMY(int8_t hours, int8_t minutes, int8_t seconds, int8_t date, int8_t month, int16_t year); // установка времени тип 2
|
|
||||||
|
|
||||||
DateTime getTime(void); // получить в структуру DateTime
|
|
||||||
uint8_t getSeconds(void); // получить секунды
|
|
||||||
uint8_t getMinutes(void); // получить минуты
|
|
||||||
uint8_t getHours(void); // получить часы
|
|
||||||
uint8_t getDay(void); // получить день недели
|
|
||||||
uint8_t getDate(void); // получить число
|
|
||||||
uint16_t getYear(void); // получить год
|
|
||||||
uint8_t getMonth(void); // получить месяц
|
|
||||||
|
|
||||||
String getTimeString(); // получить время как строку вида HH:MM:SS
|
|
||||||
String getDateString(); // получить дату как строку вида DD.MM.YYYY
|
|
||||||
void getTimeChar(char* array); // получить время как char array [8] вида HH:MM:SS
|
|
||||||
void getDateChar(char* array); // получить дату как char array [10] вида DD.MM.YYYY
|
|
||||||
|
|
||||||
bool lostPower(void); // проверка на сброс питания
|
|
||||||
float getTemperatureFloat(void);// получить температуру float
|
|
||||||
int getTemperature(void); // получить температуру int
|
|
||||||
|
|
||||||
private:
|
|
||||||
uint8_t encodeRegister(int8_t data);
|
|
||||||
int getTemperatureRaw(void);
|
|
||||||
uint8_t readRegister(uint8_t addr);
|
|
||||||
uint8_t unpackRegister(uint8_t data);
|
|
||||||
uint8_t unpackHours(uint8_t data);
|
|
||||||
const uint8_t _addr;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
Loading…
Reference in New Issue