This project turns an ATtiny85 into a small, dedicated I²C-to-DFPlayer Mini interface (or “bridge”). An external I²C master (e.g. Arduino Uno) can send simple I²C commands to the ATtiny85, which then translates them into the DFPlayer’s 10-byte serial protocol. You get full control of play, pause, stop, next, volume up/down, and specific-track selection—without burdening the master with low-level serial timing.
Key Features
• I²C Slave on ATtiny85 (address 0x42)
– Uses the USI-TWI hardware on PB0 (SDA) and PB2 (SCL)
– Pull-ups on SDA/SCL (≈4.7 kΩ) complete the bus
– TinyWireS library handles START/STOP and incoming bytes
• DFPlayer Mini Control via SoftwareSerial
– Runs at 9600 baud on PB3 (TX) and PB4 (RX) with SoftwareSerial
– Sends standard 10-byte command packets (including checksum)
– Visual feedback LED on PB1 blinks for every command sent
• Supported Commands (I²C → DFPlayer)
– Play track N
– Stop playback
– Pause & resume
– Next track
– Set absolute volume (0–30)
– Volume up/down
• Master Sketch Example
– Demonstrates how to send each command over I²C using Wire.h
– Sends one command every 2 s to showcase every DFPlayer function
How It Works
- Power-up: ATtiny85 boots at 8 MHz, initializes I²C slave on PB0/PB2 and serial port on PB3/PB4.
- Slave firmware registers a receive callback (
receiveEvent). When the master writes 1–2 bytes, the ATtiny85 decodes the command and callssendDFP(cmd,param)to issue the corresponding DFPlayer serial packet. - The master simply does
Wire.beginTransmission(0x42); Wire.write(cmd); [Wire.write(param)]; Wire.endTransmission();—no need to handle checksums or serial timing. - The DFPlayer executes each command (play, stop, volume change, etc.), and the ATtiny85 LED blinks to confirm the action.
1. ATtiny85 Slave Sketch
• Platform/Clock: ATtiny85 running at internal 8 MHz.
• DFPlayer Interface:
- Uses
SoftwareSerialon PB3 (TX → DFPlayer RX) and PB4 (RX ← DFPlayer TX) at 9 600 baud. - Sends 10-byte command packets to the DFPlayer (play, stop, pause, next track, volume up/down, etc).
- Blinks PB1 LED briefly on each command for visual feedback.
• I²C Interface: - Uses
TinyWireSUSI-TWI slave on PB0 (SDA) and PB2 (SCL), address0x42. - Implements
receiveEvent()to parse one- or two-byte commands from the master and invoke the corresponding DFPlayer action.
• Supported Commands (sent from master): 0x01 + [trk]→ Play track number[trk]0x02→ Stop playback0x03→ Pause0x04→ Resume0x05→ Next track0x06 + [vol]→ Set absolute volume[vol](0–30)0x07→ Volume up by 10x08→ Volume down by 1
#include <SoftwareSerial.h>
#include <TinyWireS.h>
#define I2C_ADDR 0x42
// DFPlayer on SoftwareSerial: TX->PB3(D3), RX<-PB4(D4)
const uint8_t DF_TX = 3;
const uint8_t DF_RX = 4;
// LED on PB1(D1) for feedback
const uint8_t LED_PIN = 1;
SoftwareSerial df(DF_RX, DF_TX);
uint8_t volumeLevel = 15; // initial mid-volume (0–30)
void sendDFP(uint8_t cmd, uint16_t param) {
uint8_t pkt[10] = {
0x7E, 0xFF, 0x06, cmd, 0x00,
uint8_t(param >> 8), uint8_t(param & 0xFF),
0, 0, 0xEF
};
uint16_t sum = 0;
for (uint8_t i = 1; i <= 6; i++) sum += pkt[i];
uint16_t chk = 0xFFFF - sum + 1;
pkt[7] = highByte(chk);
pkt[8] = lowByte(chk);
for (uint8_t i = 0; i < 10; i++) df.write(pkt[i]);
digitalWrite(LED_PIN, HIGH);
delay(30);
digitalWrite(LED_PIN, LOW);
}
// I²C receive callback: master writes one or two bytes
void receiveEvent(uint8_t howMany) {
if (howMany < 1) return;
uint8_t cmd = TinyWireS.read();
switch (cmd) {
case 0x01: { // Play track N
if (TinyWireS.available()) {
uint8_t trk = TinyWireS.read();
sendDFP(0x03, trk);
}
break;
}
case 0x02: // Stop
sendDFP(0x16, 0x0000);
break;
case 0x03: // Pause
sendDFP(0x0E, 0x0000);
break;
case 0x04: // Resume
sendDFP(0x0D, 0x0000);
break;
case 0x05: // Next track
sendDFP(0x01, 0x0000);
break;
case 0x06: { // Set volume V
if (TinyWireS.available()) {
uint8_t vol = TinyWireS.read();
if (vol > 30) vol = 30;
volumeLevel = vol;
sendDFP(0x06, volumeLevel);
}
break;
}
case 0x07: // Volume up
if (volumeLevel < 30) volumeLevel++;
sendDFP(0x06, volumeLevel);
break;
case 0x08: // Volume down
if (volumeLevel > 0) volumeLevel--;
sendDFP(0x06, volumeLevel);
break;
// add more DFPlayer commands here if needed
}
}
void setup() {
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW);
// 1) init I²C slave on PB0/PB2
TinyWireS.begin(I2C_ADDR);
TinyWireS.onReceive(receiveEvent);
// 2) init DFPlayer serial on PB3/PB4
df.begin(9600);
delay(200);
}
void loop() {
TinyWireS_stop_check();
// no further background tasks
}
2. Arduino Master Sketch
How it works
- Serial Input
- You type commands into the Serial Monitor (115 200 baud).
- The code reads a line, splits into a text keyword and optional integer.
- Command->I²C
- Depending on the keyword, it calls
send1(cmd)orsend2(cmd,param), which wrapWire.beginTransmission()….endTransmission(). - These send 1–2 bytes to the ATtiny85 at address
0x42.
- Depending on the keyword, it calls
- ATtiny85 Slave
- Its
receiveEvent()sees the incoming byte(s) and invokessendDFP()with the appropriate DFPlayer command code and parameter.
- Its
With this in place, you simply open Serial Monitor, type “play 1” to hear track 1, “vol 3” to set volume, “next” to skip, and so on—all via I²C → DFPlayer bridge on your ATtiny85.Create VideoCreate Practice QuestionCreate Practice Test
#include <Wire.h>
const uint8_t SLAVE_ADDR = 0x42;
// Text commands mapping to slave codes:
#define CMD_PLAY_TRACK 0x01
#define CMD_STOP 0x02
#define CMD_PAUSE 0x03
#define CMD_RESUME 0x04
#define CMD_NEXT 0x05
#define CMD_SET_VOL 0x06
#define CMD_VOL_UP 0x07
#define CMD_VOL_DOWN 0x08
void setup() {
Serial.begin(115200);
while (!Serial) {
;
}
Wire.begin();
Serial.println(F("I2C Master ready. Enter commands:"));
Serial.println(F(" play N stop pause resume"));
Serial.println(F(" next vol N up down"));
}
void loop() {
if (!Serial.available()) return;
String line = Serial.readStringUntil('\n');
line.trim();
if (line.length() == 0) return;
// Tokenize
char cmd[16];
int val = 0;
int args = sscanf(line.c_str(), "%15s %d", cmd, &val);
if (strcasecmp(cmd, "play") == 0 && args == 2) {
send2(CMD_PLAY_TRACK, val);
Serial.print(F("→ Play track ")); Serial.println(val);
}
else if (strcasecmp(cmd, "stop") == 0) {
send1(CMD_STOP);
Serial.println(F("→ Stop"));
}
else if (strcasecmp(cmd, "pause") == 0) {
send1(CMD_PAUSE);
Serial.println(F("→ Pause"));
}
else if (strcasecmp(cmd, "resume") == 0) {
send1(CMD_RESUME);
Serial.println(F("→ Resume"));
}
else if (strcasecmp(cmd, "next") == 0) {
send1(CMD_NEXT);
Serial.println(F("→ Next track"));
}
else if (strcasecmp(cmd, "vol") == 0 && args == 2) {
send2(CMD_SET_VOL, val);
Serial.print(F("→ Set volume ")); Serial.println(val);
}
else if (strcasecmp(cmd, "up") == 0) {
send1(CMD_VOL_UP);
Serial.println(F("→ Volume up"));
}
else if (strcasecmp(cmd, "down") == 0) {
send1(CMD_VOL_DOWN);
Serial.println(F("→ Volume down"));
}
else {
Serial.println(F("Unknown command"));
}
}
// Send one‐byte command
void send1(uint8_t c) {
Wire.beginTransmission(SLAVE_ADDR);
Wire.write(c);
Wire.endTransmission();
}
// Send two‐byte command + parameter
void send2(uint8_t c, uint8_t p) {
if (p > 30) p = 30; // clamp volume if needed
Wire.beginTransmission(SLAVE_ADDR);
Wire.write(c);
Wire.write(p);
Wire.endTransmission();
}
3. Wiring Diagram

ATtiny85 (DIP-8, flat side up) connections:
• Pin 8 (VCC) → +5 V
• Pin 4 (GND) → GND
• PB0 (D0, pin 5) → I²C SDA ←→ master SDA (A4)
– 4.7 kΩ pull-up resistor from PB0 to +5 V
• PB2 (D2, pin 7) → I²C SCL ←→ master SCL (A5)
– 4.7 kΩ pull-up resistor from PB2 to +5 V
• PB3 (D3, pin 2) → DFPlayer “RX” pin (SoftwareSerial TX)
• PB4 (D4, pin 3) → DFPlayer “TX” pin (SoftwareSerial RX)
• PB1 (D1, pin 6) → LED (with 220 Ω resistor) → GND for visual feedback
DFPlayer Mini connections:
• VCC → +5 V (shared)
• GND → GND (shared)
• RX → PB3 (ATtiny85)
• TX → PB4 (ATtiny85)
Arduino (e.g. Uno) master I²C:
• SDA (A4) → PB0 (ATtiny85)
• SCL (A5) → PB2 (ATtiny85)
• GND → common GND
Note: Ensure all devices share the same 5 V and ground. The two 4.7 kΩ pull-ups on SDA/SCL complete the I²C bus.
With this setup you can fully control your DFPlayer Mini—play, stop, change volume, etc.—via simple I²C commands from your Arduino master, while the ATtiny85 offloads the bit-banged serial protocol.
Why Use This Bridge?
• Offloads timing-critical DFPlayer serial from your main Arduino or host CPU.
• Exposes a simple, byte-oriented I²C interface—ideal for high-level control or distributed systems.
• Fits into 8-pin DIP and uses only five pins (two for I²C, two for serial, one for LED).
In one small ATtiny85 you get a robust, easy-to-use I²C gateway to your DFPlayer Mini—perfect for projects where you want to delegate audio playback to a tiny coprocessor.