Differences

This shows you the differences between two versions of the page.

Link to this comparison view

pm:prj2026:jan.vaduva:dragos.ciobanu1706 [2026/05/18 17:48]
dragos.ciobanu1706
pm:prj2026:jan.vaduva:dragos.ciobanu1706 [2026/05/25 02:04] (current)
dragos.ciobanu1706 [Code]
Line 9: Line 9:
 ===The project is split in 2 parts: The dart and The board:=== ===The project is split in 2 parts: The dart and The board:===
 == The Dart == == The Dart ==
-I am using an ESP32 for this since is has built-in wi-fi for wireless communication with my PC and Free_RTOS for light sleep functionality. The acceleration and gyroscopic angles are measured by a MPU6050, that has a motion detection interrupt available. Using the interrupt, I can just alert the board when a throw is in process. This way, my ESP can stand by in light sleep most of the time, preserving energy. When a throw is detected, the accelerometer reads data until it reaches maximum velocity, then the board sends the throw data to my PC via wi-fi. At the same time, a distance measurement protocol activates: the dart "​syncs"​ with the board, using an infra red LED and a ultrasound distance measurement unit. The distance from the board is necessary for the trajectory calculation.+I am using an ESP32 for this since is has built-in wi-fi for wireless communication with my PC and Free_RTOS for light sleep functionality. The acceleration and gyroscopic angles are measured by a MPU6050, that has a motion detection interrupt available. Using the interrupt, I can just alert the board when a throw is in process. This way, my ESP can stand by in light sleep most of the time, preserving energy. When a throw is detected, the accelerometer reads data until it reaches maximum velocity, then the board sends the throw data to my PC via bluetooth. At the same time, a distance measurement protocol activates: the dart "​syncs"​ with the board, using an infra red LED and a ultrasound distance measurement unit. The distance from the board is necessary for the trajectory calculation.
 == The Board == == The Board ==
 Since I only have one ESP board, I will be using my laptop to receive the data from the dart and transmit it along to my Arduino controlling the board, using UART. If needed, the PC can also process the data before sending it. For the visual feedback of the throw, I will be having 82 LEDs attached to the dart board. One for each segment. When the dart would hit a segment on the board, the corresponding LED lights up. I am multiplexing them by connecting a pair of antiparallel LEDs between each of the pins used. That way, with only 10 pins, I can control up to 90 LEDs, of which I will only be using 82 Since I only have one ESP board, I will be using my laptop to receive the data from the dart and transmit it along to my Arduino controlling the board, using UART. If needed, the PC can also process the data before sending it. For the visual feedback of the throw, I will be having 82 LEDs attached to the dart board. One for each segment. When the dart would hit a segment on the board, the corresponding LED lights up. I am multiplexing them by connecting a pair of antiparallel LEDs between each of the pins used. That way, with only 10 pins, I can control up to 90 LEDs, of which I will only be using 82
Line 17: Line 17:
  
 ===== Hardware Design ===== ===== Hardware Design =====
-^   ​Component ​        ​^ ​ Count  ^  Role                                                                  ^ +^   ​Component ​        ​^ ​ Count  ​^ ​ PIN  ​^ ​ Role                                                                  ^ 
-| ESP32               | 1       ​| Dart MCU: Wi-Fi data transmission to PC, FreeRTOS light sleep standby ​ | +| ESP32               | 1    ​| ​            ​N/​A ​             ​| Dart MCU: Wi-Fi data transmission to PC, FreeRTOS light sleep standby ​ | 
-| MPU6050 ​            | 1       ​| Measures dart acceleration & gyroscopic angles; triggers motion interrupt to wake ESP32 | +| MPU6050 ​            | 1    | E-GPIO21(SDA)/​E-GPIO22(SCL)  ​|Measures dart acceleration & gyroscopic angles; triggers motion interrupt to wake ESP32| 
-| IR LED              | 1       ​| Dart-side sync signal for distance measurement protocol ​               | +| IR LED              | 1    ​| ​         A-GPIO13 ​           ​| Dart-side sync signal for distance measurement protocol ​               | 
-| Ultrasound Sensor ​  | 2       ​| Measures dart-to-board distance for trajectory calculation ​            | +| Ultrasound Sensor ​  | 2    | E-GPIO16(Trig)/​A-GPIO12(Echo) ​| Measures dart-to-board distance for trajectory calculation ​            | 
-| Arduino ​            | 1       ​| Board MCU: receives processed data from PC via UART, controls board mechanisms | +| Arduino ​            | 1    ​| ​            ​N/​A ​             ​| Board MCU: receives processed data from PC via UART, controls board mechanisms | 
-| LEDs                | ?       | Board lighting: illuminate scored segments ​                            | +| LEDs                | 82   ​| ​    ​A-GPIO2->​A-GPIO11 ​       ​| Board lighting: illuminate scored segments ​                            | 
-| LED Screen ​         | 1       ​| Displays score, game state, or throw feedback ​                         |+| LED Screen ​         | 1    ​| ​        ​A-SDA/​A-SCL ​         ​| Displays score, game state, or throw feedback ​                         |
  
 ===== Software Design ===== ===== Software Design =====
-The firmware for both the ESP32 and the Arduino is developed using PlatformIO. The ESP32 runs on top of FreeRTOS, spending most of its time in light sleep until the MPU6050 motion interrupt signals a throw. ​Current libraries in use include ​the ESP-IDF GPIO and sleep drivers, ​custom I2C driver, and the FreeRTOS task schedulerAdditional libraries will be integrated as the design progresses+ 
-The Arduino communicates ​with the PC through UARTreceiving processed throw data and translating it into board actions.+The firmware for both the ESP32 and the Arduino is developed using PlatformIO. The ESP32 runs on top of FreeRTOS, spending most of its time in light sleep until the MPU6050 motion interrupt signals a throw. ​The Arduino side uses bare-metal avr-libc, without ​the Arduino library, for direct register-level control of the charlieplexed LED matrix. 
 + 
 +=== Libraries and Frameworks === 
 + 
 +== The Dart (ESP32 / ESP-IDF + FreeRTOS) == 
 +  * **ESP-IDF I2C Master Driver** (''​driver/​i2c_master.h''​) — communicates with the MPU6050 over I2C (GPIO21 SDA, GPIO22 SCL, 100 kHz) 
 +  * **ESP-IDF GPIO Driver** (''​driver/​gpio.h''​) — configures GPIO34 as an interrupt input for the MPU6050 motion detect signal 
 +  * **ESP-IDF Sleep API** (''​esp_sleep.h''​) — manages light sleep between throws to conserve power 
 +  * **FreeRTOS Semaphore** — an ISR-safe binary semaphore synchronizes the motion interrupt with the main game loop task 
 +  * **NimBLE (esp-nimble-cpp v2.5.0)** — BLE GATT server that exposes ​single Notify characteristic for streaming throw data to the PC 
 + 
 +== The Handler (Python / PC) == 
 +  * **Bleak** — asynchronous BLE client libraryconnects to the Dart controller ​and subscribes to throw notifications 
 + 
 +== The Board (ATmega328P / Bare-metal AVR) == 
 +  * **avr-libc** — direct port/pin register manipulation for charlieplexing 
 +  * Custom **charlieplex_dartboard** driver — maps all 82 segments to rail pairs stored in PROGMEM, minimizing RAM usage on the 2 KB ATmega328P 
 + 
 +=== Firmware === 
 + 
 +== The Dart == 
 + 
 +**MPU6050 Initialization:​** 
 +The sensor is configured for ±8g accelerometer range, ±500°/s gyroscope range, with a digital low-pass filter at 94 Hz. The motion detection interrupt is set to trigger at 0.50g with a 1 ms duration threshold:​ 
 + 
 +<code c> 
 +void init_mpu() { 
 +    i2c_write(0x6B,​ 0x00); // Wake up the MPU-6050 
 +    i2c_write(0x1A,​ 0x02); // Set DLPF to 94Hz 
 + 
 +    i2c_write(0x1B,​ 0x08); // Set gyro full scale to ±500°/s 
 +    i2c_write(0x1C,​ 0x18); // Set accelerometer full scale to ±8g 
 + 
 +    i2c_write(0x1F,​ 0x04); // Set motion threshold (~0.51g) 
 +    i2c_write(0x20,​ 0x01); // Set motion duration to 1ms 
 + 
 +    i2c_write(0x37,​ 0x20); // Latch mode until interrupt cleared 
 +    i2c_write(0x38,​ 0x40); // Enable Motion Interrupt 
 +
 +</​code>​ 
 + 
 +**Interrupt-driven throw detection:​** 
 +When the MPU6050 detects motion above the threshold, it asserts its INT pin. An ISR on GPIO34 gives a FreeRTOS ​binary semaphore, waking the main game task without polling: 
 + 
 +<code c> 
 +static SemaphoreHandle_t throwSemaphore;​ 
 + 
 +static void IRAM_ATTR mpu_isr_handler(void* arg) { 
 +    BaseType_t xHigherPriorityTaskWoken = pdFALSE; 
 +    xSemaphoreGiveFromISR(throwSemaphore,​ &​xHigherPriorityTaskWoken);​ 
 +    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);​ 
 +
 +</​code>​ 
 + 
 +**Main game loop:** 
 +The task blocks indefinitely on the semaphoreWhen a throw is detected, it reads 6 accelerometer and gyroscope values, packs them into a 24-byte float array, and sends it to the PC via BLE Notify: 
 + 
 +<code c> 
 +while(1) { 
 +    xSemaphoreTake(throwSemaphore,​ portMAX_DELAY);​ 
 + 
 +    i2c_read(0x3A,​ buff); // Clear interrupt flag 
 +    read_mpu_acc(&​accX,​ &accY, &​accZ);​ 
 +    read_mpu_gyro(&​gyroX,​ &gyroY, &​gyroZ);​ 
 + 
 +    float payload[6] = {accX, accY, accZ, gyroX, gyroY, gyroZ}; 
 +    pCharacteristic->​setValue((uint8_t*)payload,​ sizeof(payload));​ 
 +    pCharacteristic->​notify();​ 
 +
 +</​code>​ 
 + 
 +== The Handler == 
 +The Python handler bridges the dart and the board. A background thread runs the Bleak BLE event loop, and each notification triggers a hitscan calculation. 
 + 
 +**Hitscan and scoring:** 
 +The accelerometer Y and Z values are scaled by a sensitivity constant to produce impact coordinates in mm. The scoring function uses polar coordinates to determine the ring (bullseye, single, triple, double, miss) and sector (standard 20-sector layout): 
 + 
 +<code python>​ 
 +HITSCAN_SENSITIVITY = 27.5 
 + 
 +def calculate_hitscan(acc_y,​ acc_z): 
 +    impact_x = acc_y * HITSCAN_SENSITIVITY 
 +    impact_y = acc_z * HITSCAN_SENSITIVITY 
 +    return impact_x, impact_y 
 + 
 +def get_dartboard_score(x_mm,​ y_mm): 
 +    distance = math.sqrt(x_mm**2 + y_mm**2) 
 +    if distance <= 6.35:  return "Inner Bullseye (50)"​ 
 +    if distance <= 15.9:  return "Outer Bullseye (25)"​ 
 +    if distance > 170:    return "Miss (0)" 
 + 
 +    angle = math.degrees(math.atan2(y_mm,​ x_mm)) 
 +    if angle < 0: angle += 360 
 +    sector_angle = (angle + 9) % 360 
 +    sector_index = int(sector_angle / 18) 
 + 
 +    sectors = [6, 10, 15, 2, 17, 3, 19, 7, 16, 8, 
 +               11, 14, 9, 12, 5, 20, 1, 18, 4, 13] 
 +    base = sectors[sector_index] 
 + 
 +    if 99 <= distance <= 107:  return f"​Triple {base} ({base * 3})" 
 +    if 162 <= distance <= 170: return f"​Double {base} ({base * 2})" 
 +    return f"​Single {base} ({base})"​ 
 +</​code>​ 
 + 
 +== The Board == 
 +The board firmware uses a charlieplexing driver to control 82 LEDs with only 10 GPIO pins (Arduino D3–D12, mapped to AVR ports PD3–PD7 and PB0–PB4). 
 + 
 +**Segment-to-rail mapping:​** 
 +A PROGMEM lookup table stores ​the (HIGH railLOW rail) pair for each of the 82 dartboard segments. The mapping was derived from the Eagle schematic, where each diode'​s anode and cathode pins were cross-referenced with the physical ​board layout: 
 + 
 +<code c> 
 +static const struct { 
 +    uint8_t hi;     /* rail_t for anode (HIGH) */ 
 +    uint8_t lo;     /* rail_t for cathode (LOW) */ 
 +} seg_map[NUM_SEGMENTS] PROGMEM = { 
 +    [SEG_BEYE ​ ] = { RAIL_R6 , RAIL_R8 ​ },  /* bulseye */ 
 +    [SEG_CTR ​  ] = { RAIL_R8 , RAIL_R6 ​ },  /* bullseye ring */ 
 +    [SEG_DBL1 ​ ] = { RAIL_R3 , RAIL_R1 ​ },  /* double points foe segment 1 */ 
 +    [SEG_DBL2 ​ ] = { RAIL_R2 , RAIL_R10 },  /* double points foe segment 2 */ 
 +    /* ... */ 
 +}; 
 +</​code>​ 
 + 
 +**LED control (''​led_on''​):​** 
 +To light a specific LED, the function reads the two rail descriptors from flash, sets exactly those two pins as OUTPUT (one HIGH, one LOW), and leaves all other pins in high-impedance. The write sequence is ordered to prevent glitches: 
 + 
 +<code c> 
 +void led_on(segment_t seg) { 
 +    if ((uint8_t)seg >= (uint8_t)NUM_SEGMENTS) { 
 +        leds_off();​ 
 +        return; 
 +    } 
 + 
 +    uint8_t hi_rail = pgm_read_byte(&​seg_map[seg].hi);​ 
 +    uint8_t lo_rail = pgm_read_byte(&​seg_map[seg].lo);​ 
 + 
 +    rail_pin_t h = read_rail(hi_rail);​ 
 +    rail_pin_t l = read_rail(lo_rail);​ 
 + 
 +    uint8_t ddr_d = 0, ddr_b = 0; 
 +    uint8_t port_d = 0, port_b = 0; 
 + 
 +    /* Anode (HIGH) */ 
 +    if (h.port_id == PORT_ID_D) { ddr_d |= h.mask; port_d |= h.mask; } 
 +    else                        { ddr_b |= h.mask; port_b |= h.mask; } 
 + 
 +    /* Cathode (LOW): OUTPUT but PORT bit stays 0 */ 
 +    if (l.port_id == PORT_ID_D) { ddr_d |= l.mask; } 
 +    else                        { ddr_b |= l.mask; } 
 + 
 +    /* Glitch-free sequence: tri-state all → set PORT → enable OUTPUT */ 
 +    DDRD  &= (uint8_t)~RAILS_MASK_D;​ 
 +    DDRB  &= (uint8_t)~RAILS_MASK_B;​ 
 +    PORTD = (uint8_t)((PORTD & ~RAILS_MASK_D) | port_d); 
 +    PORTB = (uint8_t)((PORTB & ~RAILS_MASK_B) | port_b); 
 +    DDRD |= ddr_d; 
 +    DDRB |= ddr_b; 
 +
 +</​code>​ 
 + 
 +When integrated with the Handler, the Arduino will receive a segment ID over UART (from the PC) and call ''​led_on()''​ with the corresponding segment to illuminate the hit zone.
 ===== Results ===== ===== Results =====
-In this moment, only the dart is finished. It works as intended, as you can see in the clip provided on moodle. The dartboard n screen is a simulation.+In this moment, only the dart is finished. It works as intended, as you can see in the clip provided on moodle. The dartboard n screen is a proof of concept written in python. It simulates the real life throw from a set distance and height. When the project will be in its final state, the virtual dartboard will be switched with the real one and the script will only handle data computation and transmition.
 {{ :​pm:​prj2026:​jan.vaduva:​whatsapp_image_2026-05-16_at_6.40.11_pm.jpeg?​1000 |}} {{ :​pm:​prj2026:​jan.vaduva:​whatsapp_image_2026-05-16_at_6.40.11_pm.jpeg?​1000 |}}
  
-As for the Dartboard, it's still in the making. ​After all of the LEDs have been soldered, ​I will start writing ​the code+As for the Dartboard, it's still in the making. ​All of the LEDs have been soldered, ​and the connections are almost done. The result will be a matrix of multiplexed LEDs that light up when needed. The LED screen and distance sensors well be added after this step
-{{ :​pm:​prj2026:​jan.vaduva:​whatsapp_image_2026-05-16_at_6.39.58_pm.jpeg?​1000 |}} +{{ :​pm:​prj2026:​jan.vaduva:​whatsapp_image_2026-05-24_at_9.21.58_pm.jpeg?1000 |}} 
-===== Conclusion ​=====+{{ :​pm:​prj2026:​jan.vaduva:​whatsapp_image_2026-05-24_at_9.21.58_pm_1_.jpeg?1000 |}} 
 +===== Code ===== 
 +<note tip>The whole project code, including the Dart, the Handler and the Board can be found [[https://​github.com/​dragos473/​Darts_Game.git|Here]].</​note> ​
  
 ===== Journal ===== ===== Journal =====
 +<note tip>​noting to see here for now</​note>​
 ===== Bibliography/​Resources ===== ===== Bibliography/​Resources =====
-Datasheets for ESP32, ATmega328, MPU6050 
-PM labs 
  
 +=== Datasheets ===
 +  * [[https://​www.espressif.com/​sites/​default/​files/​documentation/​esp32-wroom-32_datasheet_en.pdf|ESP32-WROOM-32 Datasheet]] — Espressif Systems, Module datasheet (pinout, electrical specifications,​ schematics)
 +  * [[https://​ww1.microchip.com/​downloads/​en/​DeviceDoc/​Atmel-7810-Automotive-Microcontrollers-ATmega328P_Datasheet.pdf|ATmega328P Datasheet]] — Microchip/​Atmel,​ Full datasheet (662 pages, register descriptions,​ electrical characteristics)
 +  * [[https://​invensense.tdk.com/​wp-content/​uploads/​2015/​02/​MPU-6000-Datasheet1.pdf|MPU-6000/​MPU-6050 Product Specification]] — InvenSense (TDK), Rev 3.4 (gyroscope/​accelerometer specs, I2C timing, pin descriptions)
 +  * [[https://​invensense.tdk.com/​wp-content/​uploads/​2015/​02/​MPU-6000-Register-Map1.pdf|MPU-6000/​MPU-6050 Register Map and Descriptions]] — InvenSense (TDK), Rev 4.0 (register addresses, bit fields, configuration values)
 +  * [[https://​docs.espressif.com/​projects/​esp-idf/​en/​stable/​esp32/​|ESP-IDF Programming Guide]] — Espressif, Official development framework for ESP32 (I2C master driver, GPIO driver, sleep API, FreeRTOS integration)
 +
 +=== Libraries ===
 +  * [[https://​github.com/​h2zero/​esp-nimble-cpp|esp-nimble-cpp]] — h2zero, C++ wrapper for the ESP32 NimBLE BLE stack (used for BLE GATT server on the dart)
 +  * [[https://​github.com/​hbldh/​bleak|Bleak]] — Henrik Blidh, Cross-platform Python BLE client library (used in the Handler for connecting to the dart)
  
 +=== Course Materials ===
 +  * PM Labs — [[https://​ocw.cs.pub.ro/​courses/​pm|Proiectarea cu Microprocesoare]],​ UPB course labs
pm/prj2026/jan.vaduva/dragos.ciobanu1706.1779115724.txt.gz · Last modified: 2026/05/18 17:48 by dragos.ciobanu1706
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0