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
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.
- Programming
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.
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.
I made the model using Fusion360.
Actual joints were made and cross-sections were checked.
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.
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.
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
- cut(laser cutter)
- 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.
See acrylic processing for more information on the process.
- Assembled and completed
Receiver side (final)
- design
Created by the same process as the Sender side.
- completion
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.
This will be attached to the counter that displays the scores.
final design
This is the target. This circle is the extent to which the sensor is completely cured.
I cut this out of drawing paper I had at home with a laser cutter.
I also included the initials of my own name.
Board
design:week6
try1
Round shape with target case design in mind.
- PCB
- Schematic
- Board
I believe a PCB design edit is needed because of the PCB layout with the board pins on top.
- Pinout
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.
try2
The pins that were facing up in try1 were edited and redesigned assuming the Piezoelectric Device was used.
- PCB
- Schematic
- Board
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)
Sender side
-
Schematic
-
pcb
Receiver side
-
Schematic
-
pcb
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
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
For more information, see week15
Data Files
- case : Link(System Integration.week15)
- Sticker : file(svg)
- Board : Link(System Integration.week15)
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.