Jamie Dowden-Duarte
Published

Papa Duckie (refactored)

Rubber duckies have been a collectible and a symbolic anecdote to my coding experience, that I decided to make Papa duckie!

IntermediateWork in progressOver 1 day183
Papa Duckie (refactored)

Things used in this project

Hardware components

NeoPixel Ring: WS2812 5050 RGB LED
Adafruit NeoPixel Ring: WS2812 5050 RGB LED
×1
Speaker: 3W, 4 ohms
Speaker: 3W, 4 ohms
×1
DFPlayer - A Mini MP3 Player
DFRobot DFPlayer - A Mini MP3 Player
×1
Temp&Humi&Barometer Sensor (BME280)
Seeed Studio Temp&Humi&Barometer Sensor (BME280)
×1
Adafruit PyPortal - CircuitPython Powered Internet Display
Adafruit PyPortal - CircuitPython Powered Internet Display
×1
Rotary Encoder with Push-Button
Rotary Encoder with Push-Button
×1
Switch Actuator, Head for spring return push-button
Switch Actuator, Head for spring return push-button
×1
Photon
Particle Photon
×1

Software apps and online services

Visual Studio Code Extension for Arduino
Microsoft Visual Studio Code Extension for Arduino

Hand tools and fabrication machines

Laser cutter (generic)
Laser cutter (generic)
Soldering iron (generic)
Soldering iron (generic)
Hot glue gun (generic)
Hot glue gun (generic)
3D Printer (generic)
3D Printer (generic)

Story

Read more

Custom parts and enclosures

PapaDuckie

In all his glory

3D duckie timelapse!!!

Tub box

design

design

Schematics

Updating Schematics

UPDATED fritzing

Going through and improving my past projects Papa duckie is close to my heart so I am starting with him

schematics

Fritzing

Code

papaDuckieRefactored

C/C++
/*
 * Project PapaDuck refactored
 * Author: Jamie Dowden-Duarte
 * Date: 10/24/25
 * For comprehensive documentation and examples, please visit:
 * https://docs.particle.io/firmware/best-practices/firmware-template/
 */

#include "Particle.h"
#include "IoTClassroom_CNM.h"
#include "math.h"
#include "neopixel.h"
#include "Adafruit_ssd1306.h"
#include "Adafruit_BME280.h"
#include "Encoder.h"
#include "DFRobotDFPlayerMini.h"

SYSTEM_MODE(MANUAL);

const int PIXELCOUNT = 12, OLED_RESET = -1, HEXADDRESS = 0X76;
const int SEALEVELPRESSURE_HPA = 1013.25, DEG = 0XB0, PERCENT = 0X25;
const int PINA = D4, PINB = D5, PBUTTON = D3, ESWTCH = D15, p2Button = D13;
const int HUES[] = {1, 2, 3, 4, 5, 6};
const int WEMOS[] = {0, 1, 2, 3, 4, 5};
const int SATURATION = 255;
const int MAXENCPOS = 96, MAXBRITE = 255, MAXVOLUME = 30;
bool wemowrite, buttonState, switchState, player, status, playSong;
unsigned long lastUpdateTime = 0, currentTime;
float temp = 0.0, volume;
int color = rainbow[0], hue = HueRainbow[color % 7];
int i, timer, wemo, huey, brightness, position, lastpos;
bool onOff, playPause;

#define WIDTH 59
#define HEIGHT 64

const unsigned char duckie[] PROGMEM = {
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x07, 0xFC, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x0C, 0x07, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x30, 0x01, 0x80, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x60, 0x00, 0xC0, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x40, 0x00, 0x60, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x80, 0x00, 0x20, 0x00, 0x00, 0x00,
    0x00, 0x01, 0x80, 0x00, 0x30, 0x00, 0x00, 0x00,
    0x00, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
    0x00, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
    0x00, 0x01, 0x06, 0x00, 0x10, 0x00, 0x00, 0x00,
    0x00, 0x01, 0x0F, 0x00, 0x10, 0x00, 0x00, 0x00,
    0x00, 0x03, 0xC7, 0x00, 0x10, 0x00, 0x00, 0x00,
    0x00, 0x06, 0x60, 0x00, 0x10, 0x00, 0x00, 0x00,
    0x00, 0x0C, 0x30, 0x00, 0x30, 0x00, 0x00, 0x00,
    0x00, 0x08, 0x10, 0x00, 0x20, 0x00, 0x00, 0x00,
    0x00, 0x06, 0x60, 0x00, 0x61, 0xC0, 0x00, 0x00,
    0x00, 0x03, 0xE0, 0x00, 0xC3, 0x60, 0x00, 0x00,
    0x00, 0x00, 0x30, 0x01, 0xDE, 0x30, 0x00, 0x00,
    0x00, 0x00, 0x60, 0x03, 0xF0, 0x18, 0x00, 0x00,
    0x00, 0x00, 0xC0, 0x00, 0x00, 0x08, 0x00, 0x00,
    0x00, 0x00, 0x80, 0x00, 0x00, 0x0C, 0x00, 0x00,
    0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
    0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00,
    0x00, 0x02, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00,
    0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
    0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
    0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
    0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
    0x00, 0x0C, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
    0x00, 0x04, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00,
    0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
    0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
    0x00, 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
    0x00, 0x03, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00,
    0x00, 0x01, 0x80, 0x00, 0x00, 0x30, 0x00, 0x00,
    0x00, 0x00, 0xE0, 0x00, 0x00, 0x60, 0x00, 0x00,
    0x00, 0x00, 0x30, 0x00, 0x03, 0x80, 0x00, 0x00,
    0x00, 0x00, 0x0E, 0x00, 0x1E, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x03, 0xFF, 0xF0, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0};

Adafruit_NeoPixel pixel(PIXELCOUNT, SPI1, WS2812B);
Adafruit_SSD1306 display(OLED_RESET);
Adafruit_BME280 bme; // I2C
Button pushButton(PBUTTON);
Button pushButton2(p2Button);
Button encoderSwitch(ESWTCH);
Encoder encoder(PINA, PINB);
DFRobotDFPlayerMini duckiePlayer;

void setup()
{
  Serial.begin(9600);
  waitFor(Serial.isConnected, 1000);
  Serial1.begin(9600);

  WiFi.on();
  WiFi.clearCredentials();
  WiFi.setCredentials("IoTNetwork");

  WiFi.connect();
  while (WiFi.connecting())
  {
    Serial.printf(".");
  }
  Serial.printf("\n\n");

  pixel.begin();
  pixel.setBrightness(10);
  pixel.show();

  status = bme.begin(HEXADDRESS);
  if (!status)
  {
    Serial.printf("BME at address %i failed to start\n", HEXADDRESS);
  }

  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.drawBitmap(0, 0, duckie, 128, 64, WHITE);
  if (!duckiePlayer.begin(Serial1))
  { // Use softwareSerial to communicate with mp3.
    Serial.printf("Unable to begin:\n");
    Serial.printf("1.Please recheck the connection!\n");
    Serial.printf("2.Please insert the SD card!\n");
    while (true)
      ;
  }
  Serial.printf("DFPlayer Mini online.\n");
  Wire.begin();
  Serial.println("Ready to go");
}

void loop()
{
  currentTime = millis();

  // Update temperature every minute
  // if (currentTime >= 60000){
  lastUpdateTime = currentTime;
  temp = bme.readTemperature();

  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(10, 0);
  display.print("Temp: ");
  display.print(temp);
  display.print(DEG);
  display.setTextSize(2);
  display.setTextColor(WHITE);
  display.setCursor(12, 28);
  display.println("Papa Duck");
  display.display();
  // }

  // Cycle through colors on NeoPixels

  for (int i = 0; i < PIXELCOUNT; i++)
  {
    color = i % 7;
    pixel.setPixelColor(i, rainbow[color]);
    pixel.show();
    color++;
  }

  if (pushButton.isClicked())
  {
    onOff = !onOff;
    setHue(HUES[hue], onOff, HueRainbow[color % 7], brightness, SATURATION);
    duckiePlayer.volume(volume);
    if (onOff)
    {
      duckiePlayer.play();
    }
    else
    {
      duckiePlayer.stop();
    }
  }

  position = encoder.read(); // 0-96 encoder positions, 0-255 brightness

  if (encoder.read() > 95)
  {
    encoder.write(position = 95);
  }
  if (encoder.read() < 0)
  {
    encoder.write(position = 0);
  }

  if (position != lastpos)
  {
    // Serial.printf("Encoder position %i\n", position);
    brightness = position * (MAXBRITE / MAXENCPOS);
    volume = position * (MAXVOLUME / MAXENCPOS);
    // Serial.printf("Turning on Hue# %i\n", hue);
    setHue(HUES[hue], onOff, HueRainbow[color % 7], brightness, SATURATION);
    duckiePlayer.volume(volume);
    lastpos = position;
  }

  if (pushButton.isClicked())
  {
    color++;
    for (hue = 1; hue <= 6; hue++)
    {
      setHue(HUES[hue], onOff, HueRainbow[color % 7], brightness, SATURATION);
    }
  }

  if (pushButton2.isClicked())
  {
    onOff = !onOff;
    Serial.printf("onOff %i\n", onOff);
    for (wemo = 0; wemo <= 6; wemo++)
    {
      if (onOff)
      {
        Serial.printf("Turning on Wemo# %i\n", wemo);
        wemoWrite(WEMOS[wemo], HIGH);
      }

      else
      {
        Serial.printf("Turning off Wemo# %i\n", wemo);
        wemoWrite(WEMOS[wemo], LOW);
      }
    }
  }
}

Papa duck

C/C++
Classroom Remote IoT device
/*
 * Project PapaDuck
 * Author: Jamie Gavina
 * Date: 10-29-2024
 * For comprehensive documentation and examples, please visit:
 * https://docs.particle.io/firmware/best-practices/firmware-template/
 */
// demensions of box 4 long X2.5 tall inch,3 inch wide hole for plug
#include "Particle.h"
#include "Colors.h"
#include "math.h"
#include "neopixel.h"
#include "Adafruit_ssd1306.h"
#include "Adafruit_BME280.h"
#include "Wire.h"
#include "Button.h"
#include "wemo.h"
#include "Encoder.h"
#include "hue.h"
#include "DFRobotDFPlayerMini.h"

SYSTEM_MODE(MANUAL);

const int PIXELCOUNT = 12;
const int OLED_RESET = -1;
const int HEXADDRESS = 0X76;
const int SEALEVELPRESSURE_HPA = 1013.25;
const int DEG = 0XB0;
const int PERCENT = 0X25;
int color = rainbow[0];
const int WEMO5 = 5;
const int WEMO4 = 4;
const int WEMO3 = 3;
const int WEMO2 = 2;
const int WEMO1 = 1;
const int WEMO0 = 0;
bool wemowrite;
bool buttonState;
unsigned long lastUpdateTime = 0;
float temp = 0.0;
const int PINA = D4;
const int PINB = D5;
const int HUE6 = 6;
const int HUE5 = 5;
const int HUE4 = 4;
const int HUE3 = 3;
const int HUE2 = 2;
const int HUE1 = 1;
int hue = HueRainbow[color % 7];
bool switchState;
int i, timer;
bool player, status;

Adafruit_NeoPixel pixel(PIXELCOUNT, SPI1, WS2812B);
Adafruit_SSD1306 display(OLED_RESET);
Adafruit_BME280 bme; // I2C
Button pushButton(D3);
Button encoderSwitch(D15);
Encoder encoder(PINA, PINB);
DFRobotDFPlayerMini myMp3Player;

void onOff();
void hueOnOff();

void setup()
{
  Serial.begin(9600);
  waitFor(Serial.isConnected, 1000);
  Serial1.begin(9600);
  delay(1000);

  Serial.printf("DFRobot DFPlayer Mini Demo\n");
  Serial.printf("Initializing DFPlayer ... (May take 3~5 seconds)\n");

  if (!myMp3Player.begin(Serial1))
  { // Use softwareSerial to communicate with mp3.
    Serial.printf("Unable to begin:\n");
    Serial.printf("1.Please recheck the connection!\n");
    Serial.printf("2.Please insert the SD card!\n");
    while (true)
      ;
  }

  Serial.printf("DFPlayer Mini online.\n");

  Wire.begin();
  Serial.println("Ready to go");

  WiFi.on();
  WiFi.clearCredentials();
  WiFi.setCredentials("IoTNetwork");

  WiFi.connect();
  while (WiFi.connecting())
  {
    Serial.printf(".");
  }
  Serial.printf("\n\n");

  pixel.begin();
  pixel.setBrightness(10);
  pixel.show();

  bool status = bme.begin(HEXADDRESS);
  if (!status)
  {
    Serial.printf("BME at address %i failed to start\n", HEXADDRESS);
  }

  Serial.printf("DFRobot DFPlayer Mini Demo \nInitializing DFPlayer ... (May take 3~5 seconds)\n");

  status = myMp3Player.begin(Serial1, false);
  Serial.printf("Status = %i\n", status);

  if (!status)
  {
    Serial.printf("Unable to begin:\n");
    Serial.printf("1.Please recheck the connection!\n");
    Serial.printf("2.Please insert the SD card!\n");
    while (true)
      ;
  }
  Serial.printf("DFPlayer Mini online.\n");

  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
}

void loop()
{
  unsigned long currentMillis = millis();

  // Update temperature every minute
  if (currentMillis - lastUpdateTime >= 60000)
  {
    lastUpdateTime = currentMillis;
    temp = bme.readTemperature();

    display.clearDisplay();
    display.setTextSize(2);
    display.setTextColor(WHITE);
    display.setCursor(10, 0);
    display.print("Temp: ");
    display.print(temp);
    display.write(DEG);
    display.setTextSize(2);
    display.setTextColor(WHITE);
    display.setCursor(12, 28);
    display.println("Papa Duck");
    display.display();
  }

  // Cycle through colors on NeoPixels

  for (int i = 0; i < PIXELCOUNT; i++)
  {
    pixel.setPixelColor(i, color);
    color++;
    pixel.show();
  }
  delay(50); // Adjust speed of color change

  if (pushButton.isClicked())
  {
    buttonState = !buttonState;
  }
  onOff();

  if (encoderSwitch.isClicked())
  {
    switchState = !switchState;
  }
  hueOnOff();
}

void onOff()
{
  if (buttonState == TRUE)
  {
    wemoWrite(WEMO0, LOW);
    wemoWrite(WEMO1, LOW);
    wemoWrite(WEMO2, LOW);
    wemoWrite(WEMO3, LOW);
    wemoWrite(WEMO4, LOW);
    wemoWrite(WEMO5, LOW);
  }
  else
  {
    wemoWrite(WEMO0, HIGH);
    wemoWrite(WEMO1, HIGH);
    wemoWrite(WEMO2, HIGH);
    wemoWrite(WEMO3, HIGH);
    wemoWrite(WEMO4, HIGH);
    wemoWrite(WEMO5, HIGH);
  }
}

void hueOnOff()
{
  if (switchState == TRUE)
  {
    setHue(HUE1, false, HueRainbow[color % 7], random(32, 255), 160);
    setHue(HUE2, false, HueRainbow[color % 7], random(32, 255), 160);
    setHue(HUE3, false, HueRainbow[color % 7], random(32, 255), 160);
    setHue(HUE4, false, HueRainbow[color % 7], random(32, 255), 160);
    setHue(HUE5, false, HueRainbow[color % 7], random(32, 255), 160);
    setHue(HUE6, false, HueRainbow[color % 7], random(32, 255), 160);
    myMp3Player.stop();
    myMp3Player.volume(0);
  }
  else
  {
    setHue(HUE1, true, HueRainbow[color % 7], random(32, 255), 255);
    setHue(HUE2, true, HueRainbow[color % 7], random(32, 255), 255);
    setHue(HUE3, true, HueRainbow[color % 7], random(32, 255), 255);
    setHue(HUE4, true, HueRainbow[color % 7], random(32, 255), 255);
    setHue(HUE5, true, HueRainbow[color % 7], random(32, 255), 160);
    setHue(HUE6, true, HueRainbow[color % 7], random(32, 255), 160);
    hue++;
    myMp3Player.loop(1);
    myMp3Player.volume(10);
  }
}

Credits

Jamie Dowden-Duarte
5 projects • 33 followers
IOT educator | IPC SOLDER CERT | C++ | python | embedded software ENG | 3D modeling | MEP LEAN cert | SolidWorks | rapid prototyping

Comments