Skip to content

Development

Task Scheduled end date/✔︎
1 Production of sensor side base 3D printing 5/14 ✔︎
2 Produce the main board electronics design,electronics production 5/12 ✔︎
3 Make a case for the sensor side 3D design and Printing,Laser cutting 5/15 ✔︎
4 Build a case for the main board, speakers, and display. 3D design and Printing,Laser cutting 5/16 ✔︎
5 Make a sticker OR mark of the score to be placed on the table. 2D design and cutting 5/17 ✔︎
6 Thinking about how to attach the sensor to the back of the ping-pong table. thinking design 5/17 ✔︎
7 Production of parts for mounting sensors on the back of a ping-pong table  As soon as the design is decided 5/20 ✔︎
8 Write programs on the main board and the board on the sensor side, and have them communicate wirelessly via ESPNOW. write a program cade 5/21 ✔︎
9 wiring and assemble system integration 5/21 ✔︎
10 Video and slide production of the final project take a video 6/5 ✔︎
11 Final Presentation Presentation 6/9

sketch

alt text alt text

Sensor

step response

I considered a sensor that could detect when a pinball hit it. I tried step response, one of the various sensors we considered, such as touch sensors and optical sensors.

see Input Devices-week9.

I designed and output a case to hold the sensor we are considering for Input Devices. I am not sure if it is responsive or not.... It is possible that it could be used, but I do not believe that it is likely.

Piezo Sensor

I was considering several of them Piezo Sensor also tried.

alt text

  • Programming

reference site

void setup() {
  Serial.begin(9600);
  pinMode(D9,OUTPUT); // LED
}

void loop() {
  int val = analogRead(A1);
  if (val > 1) {  // chenge the threshold value for the piezo sensor depend on condition
    Serial.println(val);
    digitalWrite(D9,HIGH);
    delay(100);
    digitalWrite(D9,LOW);
  }
}
  • result

The LED is solidly lit! This seems to be more responsive and usable than the STEP RESPONSE.

Try Piezo Sensor

We tested it to see if it actually responds.

We attached a Piezo Sensor element to the back of a simple case and attached it to a ping-pong table with double-sided tape.

alt text

The programming was the same as the one above; if the LEDs light up, it was a success.

It responded successfully! I think this will actually be usable, but I still need to figure out the threshold value (at this time is 800) for the piezo sensor. But for now, I'm just happy it reacted!

case

try1

This is the case of a sensor to be placed on a ping-pong table.
I have thought about what kind of case I want to make. My hope is to make it as thin as possible.

alt text

I made the model using Fusion360. alt text alt text alt text

Actual joints were made and cross-sections were checked. alt text

However, when I actually printed it, the sizes did not mesh.

try2

This case holds the sensor and board to be installed behind the table tennis table.

alt text

Now that I have decided on the actual battery to be implemented, I designed a simple design to see how big it would be and output it.

alt text

I plan to put the sensor on the back side of the ping pong table, so I thought it would not be a problem to have it this thick.

I made a lot of them, but this is what I ended up with.

Sensor side (final)

see System Integration-week15.

  • design

alt text

  • cut(laser cutter)

alt text

  • stain

This time we decided to dye transparent acrylic; on the Sensor side we used purple and blue.

note)It is important to work with good ventilation.

alt text

See acrylic processing for more information on the process.

  • Assembled and completed

alt text

Receiver side (final)

  • design

alt text

Created by the same process as the Sender side.

  • completion

alt text

Sticker

try1

I designed a sticker that will be attached to a sticker to show the score and a sticker that will be attached to the scoring board.

see computer-aided design-week2.

alt text

alt text

This will be attached to the counter that displays the scores.

alt text

final design

This is the target. This circle is the extent to which the sensor is completely cured.

alt text

I cut this out of drawing paper I had at home with a laser cutter.

alt text

I also included the initials of my own name.

alt text

Board

designweek6

try1

Round shape with target case design in mind.

  • PCB

alt text

  • Schematic

alt text

  • Board

I believe a PCB design edit is needed because of the PCB layout with the board pins on top.

alt text

  • Pinout

alt text

I had initially designed the pins not to be used, but at the time of the Asian Review, Rico advised me to keep all pins alive, so I edited them.

alt text

try2

The pins that were facing up in try1 were edited and redesigned assuming the Piezoelectric Device was used.

  • PCB

alt text

  • Schematic

alt text

  • Board

alt text

see Electronics Production-week8

Feed back

First, I will provide feedback on the tries up to week 11.
Since I want to use ESP-NOW, I will divide the board into sender and receiver parts.

Input device

  • The step response did not react as expected, so I found it difficult to control. Therefore, I decided to use a Piezoelectric Device, which has a better response. Below are the results of my attempt with the Piezoelectric Device and the specific measures I took.
    • There was variability in the values (depending on the area it was applied to).
    • I think it also changes depending on the location and environment where it is applied.
    • Instability in contact. ↪︎ Should I use a variable resistor?

Output device

  • I want to display the score on a counter, so I will use an OLED display.

    • How should I install it?
    • I need to consider the case shape and wiring.
  • Also, to avoid having to look at the display, I will use a DFPlayer.

    • How should I install it?
    • I need to consider the case shape and wiring.
    • Pay attention to the wiring (TX, RX).

Battery

  • I ues XIAO ESP32S3
    • Since the power source must be taken from the backside, I may need to make a hole to insert the battery.
    • Which battery to use (currently, I am considering using either dry cells or a LiPo battery).
    • It is important to design considering the size and the power consumption of the devices

Board(sender)

  • I will integrate an input device (Piezoelectric Device) using the XIAO ESP32S3.
  • I need to design while considering the above issues (Input device).

Board(receiver)

  • I will integrate an output device (OLED display, DFPlayer) using the XIAO ESP32S3.
  • I need to design while considering the above issues (Output device).

try3(final)

alt text

Sender side

  • Schematic alt text

  • pcb alt text

Receiver side

  • Schematic alt text

  • pcb alt text

see System Integration-week15.

Bom

Main board (receiver side)

Total : ¥1,538(JPY)

Part name Q'ty Unit Price Sub Total Where to buy
Proto Board FR1 1/4 board ¥355 ¥88 Amazon
Seeed Studio XIAO ESP32S3 1 ¥1300 ¥1300 Akizuki
PinHeader_01x03_P2.54mm_Vertical_THT_D1.4mm 2 ¥10 ¥20 Akizuki
PinHeader_01x04_P2.54mm_Vertical_THT_D1.4mm 2 ¥10 ¥20 Akizuki
LED 1206 1 ¥20 ¥20 Akizuki
Resistor 470 ohm 1 ¥50 ¥50 DigiKey
Resistor 0 ohm 2 ¥20 ¥40 DigiKey

Sensor board (Sender side)

Total : ¥1,668(JPY)

Part name Q'ty Unit Price Sub Total Where to buy
Proto Board FR1 1/4 board ¥355 ¥88 Amazon
Seeed Studio XIAO ESP32S3 1 ¥1300 ¥1300 Akizuki
PinHeader_01x03_P2.54mm_Vertical_THT_D1.4mm 4 ¥10 ¥40 Akizuki
PinHeader_01x02_P2.54mm_Vertical_THT_D1.4mm 3 ¥10 ¥30 Akizuki
LED 1206 1 ¥20 ¥20 Akizuki
Resistor 470 ohm 1 ¥50 ¥50 DigiKey
Resistor 0 ohm 7 ¥20 ¥140 DigiKey

Main case (receiver side)

Total : ¥2,137(JPY)

Part name Q'ty Unit Price Sub Total Where to buy
acrylic plate 6 ¥100 ¥600 Amazon
OLED Display 128×64 1 ¥580 ¥580 Akizuki
Speaker 1 ¥100 ¥100 Akizuki
LiPo battery 3.7V 1200mAh 1 ¥749 ¥749 Amazon
Insert Nuts 7 ¥9 ¥63 Amazon
Screw M2x4 4 ¥3 ¥12 Misumi
Washer M2 4 ¥3 ¥12 Misumi
Screw M3x8 7 ¥3 ¥21 Misumi

Sensor case (Sender side)

Total : ¥1,821(JPY)

Part name Q'ty Unit Price Sub Total Where to buy
acrylic plate 3 ¥100 ¥300 Amazon
OLED Display 128×64 1 ¥580 ¥580 Akizuki
Variable resistance 1MohmB 1 ¥60 ¥60 Akizuki
Knob for small volume 15mm 1 ¥30 ¥30 Akizuki
slide switch 1 ¥30 ¥30 Akizuki
LiPo battery 3.7V 1200mAh 1 ¥749 ¥749 Amazon
Insert Nuts 4 ¥9 ¥36 Amazon
Screw M3x6 4 ¥3 ¥12 Misumi
Screw M3x8 8 ¥3 ¥24 Misumi

Output Devices

see Output Devices-week10.

alt text

Maybe it could be used in the counter display...?

Would it be nice to have a display, or an audio count?

In the end I decided to use the OLED Display and the DFplayer Mini.

Programming

This code is final code.

I used
IDE Arduino IDE
board Xiao ESP32S3
Library(Sender)
WiFi.h A library for using the ESP32's Wi-Fi functionality, including setting the Wi-Fi mode and obtaining MAC addresses.
esp_now.h This is a library for using the ESP-NOW function, which allows direct communication between ESP32s.
Adafruit_GFX.h A graphics library from Adafruit that provides basic functionality for drawing text and graphics on the display.
Adafruit_SSD1306.h  This library is used to control OLED displays using the SSD1306 chip and is used in conjunction with Adafruit_GFX.
Library(Receiver)
SoftwareSerial.h A library for software serial communication using GPIO pins other than UART in nature (*restricted for ESP32, but may be used for communication with DFPlayer).
DFRobotDFPlayerMini.h  Library for controlling DFPlayer Mini (MP3 player module) to play, stop, and adjust sound volume.

This time I had the code rewritten using Chat GPT(GPT-4), adding the following conditions to the code in week11.

I would like to see a code added to the OLED Disply of both Reciever and Sender to display Hit!!!! and display on the OLED Disply of both the Reciever and Sender.
▼Conditions
- On the Reciever side, it should be displayed for 2 seconds as soon as DFplayer plays a sound.
- Sender side: display for 2 seconds when the piezo detects vibration and the value exceeds the value for sending to the Receiver.
- Sender side displays the value detected by the piezo for 2 seconds

Sender

// Fab Academy 2025
// Koharu Takeda
// Fab lab west-harima
// Pinball Pointer
// The MIT License (MIT)
// Xiao ESP32S3

#include <WiFi.h>
#include <esp_now.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define PIEZO_PIN A0
const int THRESHOLD = 100;

// OLED definition
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

uint8_t peerAddress[6] = { 0xCC, 0xBA, 0x97, 0x16, 0x21, 0x40 };

struct __attribute__((packed)) Payload { uint16_t value; };
Payload tx;

// Timer for display control
unsigned long hitDisplayUntil = 0;

void onSend(const uint8_t*, esp_now_send_status_t st) {
  Serial.printf("[SEND-CB] status=%d (%s)\n", st, st == ESP_NOW_SEND_SUCCESS ? "OK" : "FAIL");
}

void setup() {
  Serial.begin(115200);

  // Initialize OLED
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println("OLED init failed");
    while (true);
  }
  display.clearDisplay();
  display.setTextSize(2);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(0, 0);
  display.print("Ready");
  display.display();

  uint32_t t0 = millis();
  while (!Serial && millis() - t0 < 2000) {}

  WiFi.mode(WIFI_STA);
  delay(200);
  Serial.printf("[MAC] %s\n", WiFi.macAddress().c_str());

  ESP_ERROR_CHECK(esp_now_init());
  esp_now_register_send_cb(onSend);

  esp_now_peer_info_t p{};
  memcpy(p.peer_addr, peerAddress, 6);
  p.channel = 0;
  ESP_ERROR_CHECK(esp_now_add_peer(&p));
  Serial.println("[Wi-Fi] Peer added");
}

void loop() {
  uint16_t v = analogRead(PIEZO_PIN);
  if (v > THRESHOLD) {
    tx.value = v;
    esp_now_send(peerAddress, (uint8_t*)&tx, sizeof(tx));
    Serial.printf("[SEND] Piezo=%u  HIT!\n", v);

    // Display "Hit!!!" and the Piezo value on OLED for 2 seconds
    display.clearDisplay();
    display.setTextSize(2);
    display.setCursor(0, 0);
    display.print("Hit!!!");

    display.setCursor(0, 32); // Second line for large text
    display.printf("Piezo=%u", v);
    display.display();

    hitDisplayUntil = millis() + 2000;

    delay(15);
  } else {
    Serial.printf("[SEND] Piezo=%u\n", v);
  }

  // Clear OLED display after 2 seconds
  if (millis() > hitDisplayUntil && hitDisplayUntil != 0) {
    display.clearDisplay();
    display.display();
    hitDisplayUntil = 0;
  }

  delay(100);
}

Receiver

// Fab Academy 2025
// Koharu Takeda
// Fab lab west-harima
// Pinball Pointer
// The MIT License (MIT)
// Xiao ESP32S3

#include <WiFi.h>
#include <esp_now.h>
#include <SoftwareSerial.h>
#include <DFRobotDFPlayerMini.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define RX_PIN 44           // XIAO ESP32S3 GPIO44
#define TX_PIN 43           // XIAO ESP32S3 GPIO43
SoftwareSerial dfSer(RX_PIN, TX_PIN);
DFRobotDFPlayerMini df;

uint8_t senderMac[6] = { 0xCC, 0xBA, 0x97, 0x15, 0x47, 0x70 };
const int THRESHOLD = 30;

struct __attribute__((packed)) Payload { uint16_t value; };

volatile bool playReq = false;
uint32_t lastPlay = 0;

// OLED related
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

uint32_t oledShowUntil = 0;
String oledMessage = "";

// Display a message on the OLED for a specific duration
void showOLEDMessage(const String& msg, uint32_t duration_ms) {
  oledMessage = msg;
  oledShowUntil = millis() + duration_ms;

  display.clearDisplay();
  display.setTextSize(2);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(0, 0);
  display.println(oledMessage);
  display.display();
}

// Callback function for receiving ESP-NOW data
void onRecv(const esp_now_recv_info_t* info,
            const uint8_t* data, int len)
{
  if (len == sizeof(Payload)) {
    const Payload* p = reinterpret_cast<const Payload*>(data);
    Serial.printf("[RECV] from %02X:%02X:%02X:%02X:%02X:%02X  val=%u\n",
                  info->src_addr[0], info->src_addr[1], info->src_addr[2],
                  info->src_addr[3], info->src_addr[4], info->src_addr[5],
                  p->value);
    if (p->value > THRESHOLD) {
      playReq = true;

      // Show "Hit!" and the value on the OLED for 2 seconds
      String msg = "Hit!\nval=" + String(p->value);
      showOLEDMessage(msg, 2000);
    }
  }
}

void setup() {
  Serial.begin(115200);

  // Wait for Serial to initialize (timeout after 2 seconds)
  uint32_t t0 = millis();
  while (!Serial && millis() - t0 < 2000) {}

  /* Initialize OLED */
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println("OLED init fail");
    while (true);
  }
  display.clearDisplay();
  display.display();

  /* Initialize DFPlayer */
  dfSer.begin(9600);
  delay(1000);  // Wait for DFPlayer to boot
  if (!df.begin(dfSer)) {
    Serial.println("DFPlayer init fail → continuing without sound");
  } else {
    df.volume(25);
    Serial.println("DFPlayer ready.");
  }

  /* Initialize ESP-NOW */
  WiFi.mode(WIFI_STA);
  delay(200);  // Wait for Wi-Fi driver to start
  ESP_ERROR_CHECK(esp_now_init());
  esp_now_register_recv_cb(onRecv);

  esp_now_peer_info_t pi{};
  memcpy(pi.peer_addr, senderMac, 6);
  pi.channel = 0;
  ESP_ERROR_CHECK(esp_now_add_peer(&pi));
  Serial.println("[Wi-Fi] Peer added");
}

void loop() {
  // Play sound if triggered and at least 500 ms has passed since last play
  if (playReq && millis() - lastPlay > 500) {
    Serial.println("[PLAY] 0001.mp3");
    df.play(1);
    lastPlay = millis();
    playReq = false;
  }

  // Clear OLED display after the specified time has passed (2 seconds)
  if (oledShowUntil > 0 && millis() > oledShowUntil) {
    display.clearDisplay();
    display.display();
    oledShowUntil = 0;
  }
}

System Integration

see week15.

  • System diagram

alt text

For more information, see week15

Data Files

What I lerned

  • There are few easy successes, and failures are far more common, but the failures always lead to successes.
  • It is very difficult to overcome one's laziness.
  • It is fun and gratifying to see one's own ideas take shape.