Compare commits

..

No commits in common. "master" and "main" have entirely different histories.
master ... main

7 changed files with 37 additions and 1108 deletions

34
.gitignore vendored Normal file
View File

@ -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

View File

@ -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

3
README.md Normal file
View File

@ -0,0 +1,3 @@
# feeder
Кормушка

View File

@ -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

View File

@ -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();
}
}

View File

@ -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);
}

View File

@ -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