246   222   68    

Умное баскетбольное кольцо на основе Ардуино и Андроид-приложения

Создаем умное баскетбольное кольцо на основе Ардуино и Андроид-приложения для отслеживания прогресса игры в баскетбол. Я регулярно тренируюсь в баскетбол и всегда отслеживаю статистику - количество бросков, результативность, промахи, время тренировки и т.д. Отслеживать эти цифры довольно скучно и сложно. Кто-то может сказать, что для этого можно использовать умные мячи, но у них есть ряд недостатков:

  • Если вы хотите тренироваться с несколькими мячами, то нужно купить несколько довольно дорогих умных мячей.
  • Точность таких мячей довольно сомнительна.
  • Долговечность мяча также не долгая.

В своем проекте интеллектуального баскетбольного кольца ранее, я использовал устройство Avnet SmartEdge, чтобы протестировать процесс отслеживания тренировок по баскетболу. Мы будем использовать датчик ускорения, чтобы обнаруживать броски и датчик приближения, чтобы определять результаты. На этот раз идея этого проекта заключается в разработке окончательного решения для задачи реализации умного баскетбольного кольца на основе Ардуино. Сразу скажу, что наш проект состоит из двух основных частей - разработка на Arduino и на Android.

Шаг 1. Комплектующие

Для того, чтобы нам реализовать умное баскетбольное кольцо с использованием Ардуино, нам нужно достаточное количество комплектующих:

  • Arduino Mega 2560 или Genuino Mega 2560 × 1
  • Bluetooth Shield (шилд) × 1
  • Инфракрасный датчик приближения E18-D80NK × 1
  • Датчик вибрации Seeed Grove (SW-420) × 1
  • 8 мм RGB LED × 1
  • Резистор 10 кОм × 1
  • Резистор 100 Ом × 3
  • Резистор 47 Ом (резистор выводной (through hole resistors) - для навесного монтажа, а также монтажа в отверстия платы (объемный монтаж) × 1
  • Перезаряжаемая литий-ионная аккумуляторная батарея 15000 мАч × 1

Из программного обеспечения нам понадобятся:

Шаг 2. Arduino

Некоторые соображения по поводу оборудования, используемого в этом проекте:

  • Arduino Mega 2560. Я использовал эту плату, просто потому что она была в наличии, но вы можете использовать любую другую плату и менее дорогую, например, Arduino Uno или Arduino Nano.
  • Bluetooth Shield. Здесь то же самое, что и выше про плату. Эта штука валялась у меня дома. Но, например, Bluetooth-модуль HC-05 является более дешевым вариантом.
  • E18-D80NK инфракрасный датчик приближения. В этом проекте можно использовать несколько других датчиков приближения, но имейте в виду, что лучше использовать тот, который не подвержен влиянию солнечного света.
  • Перезаряжаемая литий-ионная батарея. Используйте любой доступный источник питания.

Схема соединений нашего проекта ниже.

Я использовал Arduino IDE для разработки кода на Arduino, запрограммированного по следующей стратегии:

  • После инициализации (переменные, светодиод, Bluetooth и т.д.) плата постоянно отслеживает состояние датчиков.
  • Если датчик приближения обнаруживает присутствие мяча, - это означает, что бросок только что произошел, и это попадание.
  • Если датчик вибрации обнаруживает какое-либо движение, - это означает, что бросок только что произошел, но он ждет 3 секунды (максимум), чтобы принять решение.
  • В это время, если датчик приближения обнаруживает присутствие мяча, он (сразу) понимает, что это попадание.
  • По истечении 3 секунд, если датчик приближения не обнаружил присутствие мяча, он понимает, что это промах.
  • Arduino информирует Android через Bluetooth о том, что бросок (попадание или промах) только что был выполнен.
  • Процесс возобновляется.

Код проект вы можете скачать или скопировать ниже:

//----------------------------------------------------------------------------//
// DEFINITIONS                                                                //
//----------------------------------------------------------------------------//

// TURN ON DEBUG MODE
// #define DEBUG
// #define DEBUG_PROX
// #define DEBUG_VIBR

//----------------------------------------------------------------------------//
// CONSTANTS                                                                  //
//----------------------------------------------------------------------------//

// PINS
const int prox_pin = 2;
const int vibr_pin = 3;
const int led_r_pin = 4;
const int led_g_pin = 5;
const int led_b_pin = 6;

// TIME
const unsigned long wait_interval = 3000;

// MATH
const float percent_to_bright_factor = 100 * log10(2) / log10(255);

//----------------------------------------------------------------------------//
// VARIABLES                                                                  //
//----------------------------------------------------------------------------//

// TIME
unsigned long wait_time;

// STATUS
boolean prox = false;
boolean vibr = false;
boolean wait = false;

//----------------------------------------------------------------------------//
// FUNCTIONS (SETTINGS)                                                       //
//----------------------------------------------------------------------------//

void setup() {
    // INITIATE PINS
    pinMode(prox_pin, INPUT);
    pinMode(vibr_pin, INPUT);
    pinMode(led_r_pin, OUTPUT);
    pinMode(led_g_pin, OUTPUT);
    pinMode(led_b_pin, OUTPUT);

    set_led(5, 100);

    // INITIATE SERIAL COMMUNICATION
    Serial.begin(9600);

    // INITIATE BLUETOOTH COMMUNICATION
    setup_bluetooth();

    set_led(4, 100);

    #ifdef DEBUG
        Serial.println("Board is alive");
        Serial.println();
    #endif
}

void setup_bluetooth() {
    #ifdef DEBUG
        Serial.println("Setting Bluetooth");
        Serial.println();
    #endif

    Serial1.begin(38400);                   // Set baud rate
    Serial1.print("\r\n+STWMOD=0\r\n");     // Set to work in slave mode
    Serial1.print("\r\n+STNA=Arduino\r\n"); // Set name
    Serial1.print("\r\n+STOAUT=1\r\n");     // Permit Paired device to connect me
    Serial1.print("\r\n+STAUTO=0\r\n");     // Auto-connection should be forbidden here
    delay(2000);                            // This delay is required.
    Serial1.print("\r\n+INQ=1\r\n");        // Make the slave inquirable 
    delay(2000);                            // This delay is required.
    while (Serial1.available()) {           // Clear data
        delay(50);
        Serial1.read();
    }
}

//----------------------------------------------------------------------------//
// FUNCTIONS (LIGHT)                                                          //
//----------------------------------------------------------------------------//

int percent_to_bright(int percent) {
    // PERCENT:
    // 0..100
    // RETURN BRIGHT
    // 255..0

    return 256 - pow(2, percent / percent_to_bright_factor);
}

void set_led(int color, int bright) {
    // COLOR:
    // 0 = GREEN
    // 1 = YELLOW  
    // 2 = RED
    // 3 = CYAN
    // 4 = BLUE
    // 5 = MAGENTA
    // 6 = WHITE
    //
    // BRIGHT:
    // 0 = OFF
    // ..
    // 100 = MAX

    #ifdef DEBUG
        Serial.println("Setting LED");
        Serial.println();
    #endif

    if (color < 0 || color > 6 || bright < 0 || bright > 100) {
        return;
    }

    int led_r_bright = 255;
    int led_g_bright = 255;
    int led_b_bright = 255;
    int bright_aux = percent_to_bright(bright);

    switch (color) {
        case 0:
            // GREEN
            led_g_bright = bright_aux;
            break;
        case 1:
            // YELLOW
            led_r_bright = bright_aux;
            led_g_bright = bright_aux;
            break;
        case 2:
            // RED
            led_r_bright = bright_aux;
            break;
        case 3:
            // CYAN
            led_g_bright = bright_aux;
            led_b_bright = bright_aux;
            break;
        case 4:
            // BLUE
            led_b_bright = bright_aux;
            break;
        case 5:
            // MAGENTA
            led_r_bright = bright_aux;
            led_b_bright = bright_aux;
            break;
        case 6:
            // WHITE
            led_r_bright = bright_aux;
            led_g_bright = bright_aux;
            led_b_bright = bright_aux;
            break;
    }

    analogWrite(led_r_pin, led_r_bright);
    analogWrite(led_g_pin, led_g_bright);
    analogWrite(led_b_pin, led_b_bright);

    return;
}

//----------------------------------------------------------------------------//
// FUNCTIONS (CHECK)                                                          //
//----------------------------------------------------------------------------//

void check_prox() {
    if (!prox) {
        if(digitalRead(prox_pin) == LOW) {
            #ifdef DEBUG_PROX
                Serial.println("Proximity detected");
                Serial.println();
            #endif

            prox = true;
            if (!vibr) {
                wait = true;
                wait_time = millis() + wait_interval;
            }
            set_shot(1);
        }
    }
}

void check_vibr() {
    if (!prox && !vibr) {
        if(digitalRead(vibr_pin) == HIGH) {
            #ifdef DEBUG_PROX
                Serial.println("Vibration detected");
                Serial.println();
            #endif

            vibr = true;
            wait = true;
            wait_time = millis() + wait_interval;
            set_led(1, 100);
        }
    }
}

void check_wait() {
    if (wait && millis() > wait_time) {
        if (!prox) {
            set_shot(0);
        }

        reset();
    }
}

//----------------------------------------------------------------------------//
// FUNCTIONS (MIS)                                                            //
//----------------------------------------------------------------------------//

void set_shot(int mode) {
    // MODE:
    // 0 = WRONG SHOT (MISS)
    // 1 = RIGHT SHOT (SCORE)

    if (mode == 0) {
        set_led(2, 100);
    } else {
        set_led(0, 100);
    }

    Serial1.print(mode);
    delay(1000);
}

void reset() {
    vibr = false;
    prox = false;
    wait = false;
    set_led(4, 100);
}

//----------------------------------------------------------------------------//
// MAIN                                                                       //
//----------------------------------------------------------------------------//

void loop() {
    check_prox();
    check_vibr();
    check_wait();
}

Шаг 3. Android

Я использовал MIT App Inventor для разработки кода Android, запрограммированного по следующей стратегии:

  • После инициализации (броски, попадания, промахи, Bluetooth и т.д.) оно ожидает нажатия кнопки «Пуск».
  • Когда кнопка «Пуск» нажата, она постоянно отслеживает соединение Bluetooth.
  • Каждый раз, когда оно получает информацию о броске, оно увеличивает соответствующий счетчик (попадание или промах) и воспроизводит нужный звук уведомления.
  • Затем оно рассчитывает проценты и обновляет табло.
  • Процесс повторяется до тех пор, пока не будут нажаты кнопки «Пауза» или «Сброс».

Код можно скачать ниже:

Шаг 4. Создание баскетбольного кольца

Это моё оригинальное баскетбольное кольцо, которое я регулярно использую для тренировок по баскетболу.

Сначала я снял пластиковую крышку под обручем и сделал отверстие для датчика приближения.

Затем я сделал маленькое отверстие для крепления датчика вибрации.

Я не мог прикрепить датчик непосредственно к пластиковой крышке из-за его кривизны, поэтому я сделал внутреннюю опору датчика, используя МДФ.

Я зафиксировал датчики приближения и вибрации, используя несколько болтов и гаек.

Затем я поместил остальную электронику в корпус.

Время протестировать устройство умного кольца.

Наконец я установил все на основу баскетбольного кольца.

Теперь пришло время всё проверить.

Шаг 5. Заключительные соображения

Система оказалась очень точной, с очень небольшим количеством ложных срабатываний и действительно редкими ложными отрицаниями.

Очень приятно играть с системой. Можно узнать статистику тренировок в режиме реального времени и после тренировки. Можно дальше проработать некоторые улучшения, добавить некоторые функции, такие как:

  • Тайм-менеджмент.
  • Функция памяти.
  • Больше данных (бросков в минуту, температура и т.д.)
  • Уведомление о достижении цели.

На этом всё. Желаю вам хорошей тренировки.

Ардуино+