Устройство Arduino, управляемое программой c # - PullRequest
0 голосов
/ 10 мая 2018

Я работаю над небольшим проектом (http://www.instructables.com/id/Digispark-Volume-Control/), в котором для управления мультимедиа используются громкоговоритель и поворотный регулятор (громкость, отключение звука, пауза, следующая, предыдущая). Громкость регулируется вращением поворотный кодер и другие элементы управляются нажатием кнопки определенным образом: 1 щелчок - для паузы, 2 щелчка - для отключения звука, короткое удержание для предыдущей песни и длительное удержание для следующей песни.

Я спрашиваю, как бы я подошел к совместному использованию c # и arduino для динамического управления этими вещами (настройте и измените то, что делают эти события, о которых я говорил выше, и, возможно, если я доберусь до этого, добавьте что-то вроде редактора макросов, который использует шаблоны от поворотных энкодеров, чтобы делать вещи). Пример для макроредактора, как идея: если я сделаю 2 коротких клика, скажем, максимум 200 мс, и поворот влево с помощью поворотного энкодера заблокирует компьютер.

Я пытаюсь выполнить что-то вроде гриффинового нападающего (https://griffintechnology.com/us/powermate), имеет программное обеспечение, которое устанавливает, что устройство делает, и устройство, которое следует программному обеспечению

Это текущий код, который я получил для устройства, он имеет только основные функции для управления мультимедиа:

#include "TrinketHidCombo.h"

#define PIN_ENCODER_A 0
#define PIN_ENCODER_B 2
#define PIN_BUTTON 1
#define TRINKET_PINx  PINB

static uint8_t enc_prev_pos = 0;
static uint8_t enc_flags    = 0;

void setup()
{
  // set pins as input with internal pull-up resistors enabled
  pinMode(PIN_ENCODER_A, INPUT);
  pinMode(PIN_ENCODER_B, INPUT);
  pinMode(PIN_BUTTON, INPUT_PULLUP);
  digitalWrite(PIN_ENCODER_A, HIGH);
  digitalWrite(PIN_ENCODER_B, HIGH);
  digitalWrite(PIN_BUTTON, HIGH);

  TrinketHidCombo.begin(); // start the USB device engine and enumerate

  // get an initial reading on the encoder pins
  if (digitalRead(PIN_ENCODER_A) == LOW) {
    enc_prev_pos |= (1 << 0);
  }
  if (digitalRead(PIN_ENCODER_B) == LOW) {
    enc_prev_pos |= (1 << 1);
  }
}

void loop()
{
  int8_t enc_action = 0; // 1 or -1 if moved, sign is direction

  // note: for better performance, the code will now use
  // direct port access techniques
  // http://www.arduino.cc/en/Reference/PortManipulation
  uint8_t enc_cur_pos = 0;
  // read in the encoder state first
  if (bit_is_clear(TRINKET_PINx, PIN_ENCODER_A)) {
    enc_cur_pos |= (1 << 0);
  }
  if (bit_is_clear(TRINKET_PINx, PIN_ENCODER_B)) {
    enc_cur_pos |= (1 << 1);
  }

  // if any rotation at all
  if (enc_cur_pos != enc_prev_pos)
  {
    if (enc_prev_pos == 0x00)
    {
      // this is the first edge
      if (enc_cur_pos == 0x01) {
        enc_flags |= (1 << 0);
      }
      else if (enc_cur_pos == 0x02) {
        enc_flags |= (1 << 1);
      }
    }

    if (enc_cur_pos == 0x03)
    {
      // this is when the encoder is in the middle of a "step"
      enc_flags |= (1 << 4);
    }
    else if (enc_cur_pos == 0x00)
    {
      // this is the final edge
      if (enc_prev_pos == 0x02) {
        enc_flags |= (1 << 2);
      }
      else if (enc_prev_pos == 0x01) {
        enc_flags |= (1 << 3);
      }

      // check the first and last edge
      // or maybe one edge is missing, if missing then require the middle state
      // this will reject bounces and false movements
      if (bit_is_set(enc_flags, 0) && (bit_is_set(enc_flags, 2) || bit_is_set(enc_flags, 4))) {
        enc_action = 1;
      }
      else if (bit_is_set(enc_flags, 2) && (bit_is_set(enc_flags, 0) || bit_is_set(enc_flags, 4))) {
        enc_action = 1;
      }
      else if (bit_is_set(enc_flags, 1) && (bit_is_set(enc_flags, 3) || bit_is_set(enc_flags, 4))) {
        enc_action = -1;
      }
      else if (bit_is_set(enc_flags, 3) && (bit_is_set(enc_flags, 1) || bit_is_set(enc_flags, 4))) {
        enc_action = -1;
      }

      enc_flags = 0; // reset for next time
    }
  }

  // Get button event and act accordingly
  int b = checkButton();
  if (b == 1) clickEvent();
  if (b == 2) doubleClickEvent();
  if (b == 3) holdEvent();
  if (b == 4) longHoldEvent();

  enc_prev_pos = enc_cur_pos;

  if (enc_action > 0) {
    TrinketHidCombo.pressMultimediaKey(MMKEY_VOL_UP);
  }
  else if (enc_action < 0) {
    TrinketHidCombo.pressMultimediaKey(MMKEY_VOL_DOWN);
  }
  else {
    TrinketHidCombo.poll(); // do nothing, check if USB needs anything done
  }
}

//=================================================
// Events to trigger

void clickEvent() {
  TrinketHidCombo.pressMultimediaKey(MMKEY_PLAYPAUSE);
}
void doubleClickEvent() {
  TrinketHidCombo.pressMultimediaKey(MMKEY_MUTE);
}
void holdEvent() {
  TrinketHidCombo.pressMultimediaKey(MMKEY_SCAN_PREV_TRACK);
}
void longHoldEvent() {
  TrinketHidCombo.pressMultimediaKey(MMKEY_SCAN_NEXT_TRACK);
}

int debounce = 20;          // ms debounce period to prevent flickering when pressing or releasing the button
int DCgap = 500;            // max ms between clicks for a double click event
int holdTime = 1000;        // ms hold period: how long to wait for press+hold event
int longHoldTime = 1500;    // ms long hold period: how long to wait for press+hold event

// Button variables
boolean buttonVal = HIGH;   // value read from button
boolean buttonLast = HIGH;  // buffered value of the button's previous state
boolean DCwaiting = false;  // whether we're waiting for a double click (down)
boolean DConUp = false;     // whether to register a double click on next release, or whether to wait and click
boolean singleOK = true;    // whether it's OK to do a single click
long downTime = -1;         // time the button was pressed down
long upTime = -1;           // time the button was released
boolean ignoreUp = false;   // whether to ignore the button release because the click+hold was triggered
boolean waitForUp = false;        // when held, whether to wait for the up event
boolean holdEventPast = false;    // whether or not the hold event happened already
boolean longHoldEventPast = false;// whether or not the long hold event happened already

int checkButton() {
  int event = 0;
  buttonVal = digitalRead(PIN_BUTTON);
  // Button pressed down
  if (buttonVal == HIGH && buttonLast == LOW && (millis() - upTime) > debounce)
  {
    downTime = millis();
    ignoreUp = false;
    waitForUp = false;
    singleOK = true;
    holdEventPast = false;
    longHoldEventPast = false;
    if ((millis() - upTime) < DCgap && DConUp == false && DCwaiting == true)  DConUp = true;
    else  DConUp = false;
    DCwaiting = false;
  }
  // Button released
  // DOUBLE CLICK
  else if (buttonVal == HIGH && buttonLast == HIGH && (millis() - downTime) > debounce)
  {
    if (not ignoreUp)
    {
      upTime = millis();
      if (DConUp == false) DCwaiting = true;
      else
      {
        event = 2;
        DConUp = false;
        DCwaiting = false;
        singleOK = false;
      }
    }
  }
  // Test for normal click event: DCgap expired
  if ( buttonVal == LOW && (millis() - upTime) >= DCgap && DCwaiting == true && DConUp == false && singleOK == true && event != 2)
  {
    event = 1;
    DCwaiting = false;
  }
  // Test for hold
  if (buttonVal == HIGH && (millis() - downTime) >= holdTime) {
    // Trigger "normal" hold
    if (not holdEventPast)
    {
      event = 3;
      waitForUp = true;
      ignoreUp = true;
      DConUp = false;
      DCwaiting = false;
      //downTime = millis();
      holdEventPast = true;
    }
    // Trigger "long" hold
    if ((millis() - downTime) >= longHoldTime)
    {
      if (not longHoldEventPast)
      {
        event = 4;
        longHoldEventPast = true;
      }
    }
  }
  buttonLast = buttonVal;
  return event;
} 

1 Ответ

0 голосов
/ 10 мая 2018

Поскольку вы не можете запускать код C # на самом Arduino, этот код должен быть запущен на ПК (или Raspberry Pi или аналогичный с использованием .NET Core).

Так что это означает, что вам нужен способ общения между ними. Ваши варианты:

  1. Отправляйте сообщения туда и обратно через последовательный порт (последовательный через USB) (проще всего, поскольку вы, вероятно, уже используете его для программирования своего Arduino).
  2. Используйте WIFI или Ethernet и создайте конечную точку WebAPI, в которую Arduino обращается для доставки сообщений и получения результатов.
  3. Используйте другой механизм связи, например, Bluetooth.

В качестве альтернативы, если у вас есть устройство, такое как Trinket, которое поддерживает эмуляцию клавиатуры только через USB, вам нужно будет выбрать несколько неиспользуемых кодов клавиш для отправки и затем подключить ваше приложение к глобальным событиям для их обработки (см. https://blogs.msdn.microsoft.com/toub/2006/05/03/low-level-keyboard-hook-in-c/) - если, конечно, ваше приложение не находится на переднем плане, в этом случае вы можете просто читать ввод с клавиатуры, когда устройство отправляет его.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...