ARDUINO Объединить две кнопки между скетчем arduino и remotexy

mrakg

✩✩✩✩✩✩✩
24 Дек 2021
2
0
Всем привет. Я пытаюсь доработать вторую версию автокормушки для кошек. Хочу сделать к ней легкое приложение. Мой выбор пал на remotexy с подключением через bluetooth модуль. Накидала дизайн в remotexy, он его превратил в код и дальше я не понимаю как его правильно приспособить в исходный код кормушки. Буду рада любой помощи)
код с remotexy:
//////////////////////////////////////////////
//        RemoteXY include library          //
//////////////////////////////////////////////

// определение режима соединения и подключение библиотеки RemoteXY
#define REMOTEXY_MODE__SOFTSERIAL
#include <SoftwareSerial.h>

#include <RemoteXY.h>

// настройки соединения
#define REMOTEXY_SERIAL_RX 2
#define REMOTEXY_SERIAL_TX 3
#define REMOTEXY_SERIAL_SPEED 9600


// конфигурация интерфейса
#pragma pack(push, 1)
uint8_t RemoteXY_CONF[] =
  { 255,7,0,0,0,212,0,13,81,1,
  1,0,26,74,12,12,2,31,208,158,
  0,129,0,12,66,40,6,31,208,191,
  208,190,208,180,208,176,209,135,208,176,
  32,208,186,208,190,209,128,208,188,208,
  176,0,129,0,11,11,42,6,65,208,
  159,209,128,208,184,208,181,208,188,209,
  139,32,208,191,208,184,209,137,208,184,
  0,129,0,8,19,48,3,66,40,209,
  131,209,129,209,130,208,176,208,189,208,
  190,208,178,208,184,209,130,208,181,32,
  208,178,209,128,208,181,208,188,209,143,
  32,208,191,209,128,208,184,208,181,208,
  188,208,190,208,178,32,208,191,208,184,
  209,137,208,184,0,7,48,22,32,20,
  5,31,26,2,7,48,22,41,20,5,
  31,26,2,7,48,22,50,20,5,31,
  26,2,129,0,17,23,34,3,66,32,
  209,129,32,209,130,208,190,209,135,208,
  189,208,190,209,129,209,130,209,140,209,
  142,32,208,180,208,190,32,209,135,208,
  176,209,129,208,190,208,178,41,0 };

// структура определяет все переменные и события вашего интерфейса управления
struct {

    // input variables
  uint8_t button_1; // =1 если кнопка нажата, иначе =0
  int16_t edit_1;  // 32767.. +32767
  int16_t edit_2;  // 32767.. +32767
  int16_t edit_3;  // 32767.. +32767

    // other variable
  uint8_t connect_flag;  // =1 if wire connected, else =0

} RemoteXY;
#pragma pack(pop)

/////////////////////////////////////////////
//           END RemoteXY include          //
/////////////////////////////////////////////



void setup()
{
  RemoteXY_Init ();


  // TODO you setup code

}

void loop()
{
  RemoteXY_Handler ();


  // TODO you loop code
  // используйте структуру RemoteXY для передачи данных
  // не используйте функцию delay()


}
код основной:
// Клик - внеочередная кормёжка
// Удержание - задаём размер порции
const byte feedTime[][2] = {
  {7, 0},       // часы, минуты. НЕ НАЧИНАТЬ ЧИСЛО С НУЛЯ
  {12, 0},
  {17, 0},
  {21, 0},
};

#define EE_RESET 12         // любое число 0-255. Измени, чтобы сбросить настройки и обновить время
#define FEED_SPEED 3000     // задержка между шагами мотора (мкс)
#define BTN_PIN 2           // кнопка
#define STEPS_FRW 18        // шаги вперёд
#define STEPS_BKW 10        // шаги назад
const byte drvPins[] = {3, 4, 5, 6};  // драйвер (фазаА1, фазаА2, фазаВ1, фазаВ2)

// =========================================================
#include "EncButton.h"
#include <EEPROM.h>
#include <RTClib.h>
RTC_DS3231 rtc;
EncButton<BTN_PIN> btn;
int feedAmount = 100;

void setup() {
  rtc.begin();
  if (EEPROM.read(0) != EE_RESET) {   // первый запуск
    EEPROM.write(0, EE_RESET);
    EEPROM.put(1, feedAmount);
    rtc.adjust(DateTime(F([B]DATE[/B]), F([B]TIME[/B])));
  }
  EEPROM.get(1, feedAmount);
  for (byte i = 0; i < 4; i++) pinMode(drvPins[i], OUTPUT);   // пины выходы
}

void loop() {
  static uint32_t tmr = 0;
  if (millis() - tmr > 500) {           // два раза в секунду
    static byte prevMin = 0;
    tmr = millis();
    DateTime now = rtc.now();
    if (prevMin != now.minute()) {
      prevMin = now.minute();
      for (byte i = 0; i < sizeof(feedTime) / 2; i++)    // для всего расписания
        if (feedTime[i][0] == now.hour() && feedTime[i][1] == now.minute())    // пора кормить
          feed();
    }
  }

  btn.tick();
  if (btn.isClick()) {
    feed();
  }
  if (btn.isHold()) {
    int newAmount = 0;
    while (btn.isHold()) {
      btn.tick();
      oneRev();
      newAmount++;
    }
    disableMotor();
    feedAmount = newAmount;
    EEPROM.put(1, feedAmount);
  }
}

void feed() {
  for (int i = 0; i < feedAmount; i++) oneRev();      // крутим на количество feedAmount
  disableMotor();
}

void disableMotor() {
  for (byte i = 0; i < 4; i++) digitalWrite(drvPins[i], 0); // выключаем ток на мотор
}

void oneRev() {
  static byte val = 0; 
  for (byte i = 0; i < STEPS_BKW; i++) runMotor(val--);
  for (byte i = 0; i < STEPS_FRW; i++) runMotor(val++);
}

void runMotor(byte thisStep) {
  /*static const byte steps[] = {0b1000, 0b1010, 0b0010, 0b0110, 0b0100, 0b0101, 0b0001, 0b1001};
    for (byte i = 0; i < 4; i++)
    digitalWrite(drvPins[i], bitRead(steps[thisStep & 0b111], i));
  */
  static const byte steps[] = {0b1010, 0b0110, 0b0101, 0b1001};
  for (byte i = 0; i < 4; i++)
    digitalWrite(drvPins[i], bitRead(steps[thisStep & 0b11], i));
  delayMicroseconds(FEED_SPEED);
}
encbutton.h:
#ifndef EncButton_h
#define EncButton_h

// =========== НАСТРОЙКИ ============
#define EB_FAST 30         // таймаут быстрого поворота
#define EB_DEB 80          // дебаунс кнопки
#define EB_HOLD 1000       // таймаут удержания кнопки
#define EB_STEP 500        // период срабатывания степ
#define EB_CLICK 400    // таймаут накликивания

// =========== НЕ ТРОГАЙ ============
#include <Arduino.h>
// флаг макро
#define _setFlag(x) (flags |= 1 << x)
#define _clrFlag(x) (flags &= ~(1 << x))
#define _readFlag(x) ((flags >> x) & 1)

// быстрое чтение пина
bool fastRead(const uint8_t pin) {
#if defined([B]AVR_ATmega328P[/B]) || defined([B]AVR_ATmega168[/B])
    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([B]AVR_ATtiny85[/B]) || defined([B]AVR_ATtiny13[/B])
    return bitRead(PINB, pin);
    
#elif defined(AVR)
    uint8_t *_pin_reg = portInputRegister(digitalPinToPort(pin));
    uint8_t _bit_mask = digitalPinToBitMask(pin);
    return bool(*_pin_reg & _bit_mask);

#else
    return digitalRead(pin);

#endif
    return 0;
}

// класс
template < uint8_t S1, uint8_t S2 = 255, uint8_t KEY = 255 >
class EncButton {
public:
    EncButton() {
        if (S2 == 255) {         // обычная кнопка
            pinMode(S1, INPUT_PULLUP);
        } else if (KEY == 255) { // энк без кнопки
            pinMode(S1, INPUT_PULLUP);
            pinMode(S2, INPUT_PULLUP);
        } else {                // энк с кнопкой
            pinMode(S1, INPUT_PULLUP);
            pinMode(S2, INPUT_PULLUP);
            pinMode(KEY, INPUT_PULLUP);
        }
    }

    void tick(bool hold = 0) {
        uint32_t thisMls = millis();
        uint32_t debounce = thisMls - _debTimer;

        // обработка энка (компилятор вырежет блок если не используется)
        if (S1 != 255 && S2 != 255) {
            byte state = fastRead(S1) | (fastRead(S2) << 1);                    // получаем код
            if (_readFlag(0) && state == 0b11) {                                 // ресет и энк защёлкнул позицию
                if (S2 == 255 || KEY != 255) {                                  // энкодер с кнопкой
                    if (!_readFlag(4)) {                                        // если кнопка не "удерживается"
                        if (_lastState == 0b10) EBState = (_btnState || hold) ? 3 : 1, counter++;
                        else if (_lastState == 0b01) EBState = (_btnState || hold) ? 4 : 2, counter--;
                    }
                } else {                                                        // просто энкодер
                    if (_lastState == 0b10) EBState = 1, counter++;
                    else if (_lastState == 0b01) EBState = 2, counter--;
                }
                if (EBState != 0 && debounce < EB_FAST) _setFlag(1);          // режим быстрого поворота
                else _clrFlag(1);
                _clrFlag(0);
                _debTimer = thisMls;
            }
            if (state == 0b00) _setFlag(0);
            _lastState = state;
        }

        // обработка кнопки (компилятор вырежет блок если не используется)
        if (S2 == 255 || KEY != 255) {
            if (S2 == 255) _btnState = !fastRead(S1);                           // обычная кнопка
            if (KEY != 255) _btnState = !fastRead(KEY);                         // энк с кнопкой

            if (_btnState) {                                                    // кнопка нажата
                if (!_readFlag(3)) {                                              // и не была нажата ранее
                    if (debounce > EB_DEB) {                                       // и прошел дебаунс
                        _setFlag(3);                                            // флаг кнопка была нажата
                        _debTimer = thisMls;                                    // сброс таймаутов
                        EBState = 0;                                               // сброс состояния
                    }
                    if (debounce > EB_CLICK) {                                    // кнопка нажата после EB_CLICK
                        clicks = 0;                                                // сбросить счётчик и флаг кликов
                        flags &= ~0b01100000;
                    }
                } else {                                                          // кнопка уже была нажата
                    if (!_readFlag(4)) {                                        // и удержание ещё не зафиксировано
                        if (debounce < EB_HOLD) {                                  // прошло меньше удержания
                            if (EBState != 0) _setFlag(2);                         // но энкодер повёрнут! Запомнили
                        } else {                                                // прошло больше времени удержания
                            if (!_readFlag(2)) {                                // и энкодер не повёрнут
                                EBState = 6;                                       // значит это удержание (сигнал)
                                _setFlag(4);                                    // запомнили что удерживается
                                _debTimer = thisMls;                            // сброс таймаута
                            }
                        }
                    } else {                                                    // удержание зафиксировано
                        if (debounce > EB_STEP) {                                  // таймер степа
                            EBState = 7;                                           // сигналим
                            _debTimer = thisMls;                                // сброс таймаута
                        }
                    }
                }
            } else {                                                            // кнопка не нажата
                if (_readFlag(3)) {                                               // но была нажата
                    if (debounce > EB_DEB && !_readFlag(4) && !_readFlag(2)) {    // энкодер не трогали и не удерживали - это клик
                        EBState = 5;
                        clicks++;
                    }
                    flags &= ~0b00011100;                                       // clear 2 3 4
                    _debTimer = thisMls;                                        // сброс таймаута
                } else if (clicks > 0 && debounce > EB_CLICK && !_readFlag(5)) flags |= 0b01100000;     // флаг на клики
            }
        }
    }

    byte getState() { return EBState; }
    void resetState() { EBState = 0; }
    bool isFast() { return _readFlag(1); }
    bool isTurn() { return (EBState > 0 && EBState < 5); }
    bool isRight() { return checkState(1); }
    bool isLeft() { return checkState(2); }
    bool isRightH() { return checkState(3); }
    bool isLeftH() { return checkState(4); }
    bool isClick() { return checkState(5); }
    bool isHolded() { return checkState(6); }
    bool isHold() { return _readFlag(4); }
    bool isStep() { return checkState(7); }
    bool state() { return !fastRead(S1); }
    bool hasClicks(byte numClicks) {
        if (clicks == numClicks && _readFlag(6)) {
            _clrFlag(6);
            return 1;
        }
        return 0;
    }
    byte hasClicks() {
        if (_readFlag(6)) {
            _clrFlag(6);
            return clicks;
        } return 0;   
    }

    int counter = 0;
    byte clicks = 0;

private:
    bool checkState(byte val) {
        if (EBState == val) {
            EBState = 0;
            return 1;
        } return 0;
    }
    uint32_t _debTimer = 0;
    byte _lastState = 0, EBState = 0;
    bool _btnState = 0;
    byte flags = 0;   

    // flags
    // 0 - enc reset
    // 1 - enc fast
    // 2 - enc был поворот
    // 3 - флаг кнопки
    // 4 - hold
    // 5 - clicks flag
    // 6 - clicks get
    // 7 - reserved

    // EBState
    // 0 - idle
    // 1 - right
    // 2 - left
    // 3 - rightH
    // 4 - leftH
    // 5 - click
    // 6 - holded
    // 7 - step
};

#endif
 
Изменено:

PiratFox

★★★★★✩✩
13 Фев 2020
1,703
474
@mrakg, так поместите свой код в loop и setup, затем используйте переменные из уже готовой структуры (button_1, edit_1 и т.д.).
 

mrakg

✩✩✩✩✩✩✩
24 Дек 2021
2
0
@PiratFox, это я понимаю, но не понимаю как кнопке с remotexy сделать такой же функционал, как у кнопки с основного кода