Welcome to Hackster!
Hackster is a community dedicated to learning hardware, from beginner to pro. Join us, it's free!
arduinocc
Published © GPL3+

The Talking IoT Toaster

If you've ever felt people don't ask for the all toast they want, this is the project for you!

AdvancedFull instructions provided2,238
The Talking IoT Toaster

Things used in this project

Hardware components

Arduino Nano R3
Arduino Nano R3
×1
PIR Motion Sensor (generic)
PIR Motion Sensor (generic)
×1
SparkFun Sound Detector (with Headers)
SparkFun Sound Detector (with Headers)
Or Similar
×1
Speaker: 0.25W, 8 ohms
Speaker: 0.25W, 8 ohms
×1
Audio Amplifier Board (Based on HXL1446)
×1
Development Board, Class D Audio Amplifier Module
Development Board, Class D Audio Amplifier Module
×1
Wire, Hook Up
Wire, Hook Up
×2
High Brightness LED, White
High Brightness LED, White
×1

Software apps and online services

Arduino IDE
Arduino IDE
Visual Studio 2017
Microsoft Visual Studio 2017
Visual Micro
Audacity
FreePCB
ONLY if building PCB
GerbV
ONLY if building PCB
Inkscape
ONLY if building PCB
JTech Photonics Inkscape Laser Plug-In
ONLY if building PCB

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
Solder Wire, Lead Free
Solder Wire, Lead Free
Solder Flux, Soldering
Solder Flux, Soldering
Single Side Copper Clad Board
Matt Black Spray Paint
Hacksaw
3D Printer (generic)
3D Printer (generic)
2.5W UV Laser Module
Orange 3D Printer Filament
Can use Orange spray Paint as we did
Super Glue

Story

Read more

Custom parts and enclosures

Speaker Cover

STL File for 3D Printing Speaker Cover Frame (quick design)
Supports: Yes
Infill: 20%

Schematics

Image of PCB Design

Just an image to show the PCB design, read tutorial for where current issues had to be worked around.

Code

TalkieToaster.ino

Arduino
Main code file to run the project.....
/*

Lib: https://github.com/TMRh20/TMRpcm

Talkie made by Crapola Inc --> https://reddwarf.fandom.com/wiki/Crapola_Inc.

STL File for Block Lever for a Toaster (Talkie v1 needs two block ones)
https://www.thingiverse.com/thing:63723

Episodes Talkie is in: https://reddwarf.fandom.com/wiki/Talkie_Toaster
V1 Talkie:
In two episodes of Series I, "Future Echoes" and "Waiting for God", Talkie Toaster appears as a standard 1970s toaster made from stainless steel but with a circular light on the side which flashes as he speaks, and is voiced by John Lenahan.
In "Balance of Power", Series I - Lister eats Krispies and converses with Talkie Toaster in the sleeping quarters of Red Dwarf
In a deleted scene from the first episode of Series II, Talkie Toaster is seen singing a duet with Cat (this scene can be seen in the special features of the Series II DVD.)
V2 Talkie:
In the Series IV episode "White Hole", Talkie Toaster appears rebuilt by Kryten with a housing of red plastic, with his name "Talkie Toaster" emblazoned on the side, and rather more flashing red and green lights (originally from "Box", the computer in Star Cops) - although they have nothing to do with when he speaks, which is indicated by the press-down handle that would usually be used to start bread toasting - and is voiced by David Ross (who originally played Kryten in Series II). The second novel's description matches his appearance in Series IV.

Nano - Arduino Pins
SD Card
13 sck
12 miso
11 mosi
10 cs

3  2x10mm LED to sync with sound
4 PIR input
5 Mic Input for Listenting for replies
9 Speaker output, to amplifier


*/


#include <SPI.h>
#include <TMRpcm.h>     
#include <pcmRF.h>
#include <pcmConfig.h>
#include <SD.h>

#define SD_CARD 10  

TMRpcm sample;

// PIR sensor
int calibrationTime = 10;
				
long conversationQuitUs = 60000; // If no-one continues within a minute - quit conversation
long conversationStartTime = 0;
int pirPin = 4;    // PIR sensor input
int micPin = 5;    // Mic sensor Input
int ledPin = 3;    // LED outputs 
int speakerInputPin = 2; // Feeback from output of speaker for LEDs....

char* currentPlaylist = "1";
int currentTrack = 0;
boolean triggered = false;
boolean pirTriggered = false;
boolean micTriggered = false;
boolean talking = false;


/* Sound Files:
1-0_1.wav			Talkiev1 - 1 - You Cant Sing
1-0_2.wav			Talkiev1 - 2 - Just cos Im a Toaster
1-0_3noBang.wav			Talkiev1 - 3 - Sings Sinatra (no  Bang)
1-0_3withBang.wav			Talkiev1 - 3 - Sings Sinatra (with Bang)

2-0_1.wav			Talkiev2 - 1 - Intro
2-0_2.wav			Talkiev2 - 2 - How About a Muffin
2-0_3.wav			Talkiev2 - 3 - Waffle Man
2-0_4.wav			Talkiev2 - 4 - Can I ask One Question
2-0_5.wav			Talkiev2 - 5 - Any Toast
2-0_6.wav			Talkiev2 - 6 - Changed Your Mind
2-0_7.wav			Talkiev2 - 7 - I Toast Therefore I Am
2-1_1.wav			Talkie v2.1 - 1 - Hope You don't get an overload

2-1_2.wav			Talkie v2.1 - 2 - Youll Explode
2-1_4.wav			Talkie v2.1 - 4 - Any Question
2-1_5.wav			Talkie v2.1 - 5 - Any Question
2-1_6.wav			Talkie v2.1 - 6 - Ok Heres my Question
2-1_7.wav			Talkie v2.1 - 7 - Chaos Theory
2-1_8.wav			Talkie v2.1 - 8 - Would you like a Crumpet
2-1_9.wav			Talkie v2.1 - 9 - That is not answering
2-1_10.wav			Talkie v2.1 - 10 - A Sensible Question
2-1_11.wav			Talkie v2.1 - 11 - Resent Implication
2-1_12.wav			Talkie v2.1 - 12 - God Toasted Teacake
2-1_13.wav			Talkie v2.1 - 13 - Before You go
2-1_14.wav			Talkie v2.1 - 14 - Cheese and Ham Braville
*/

char* playlist[22][3] = {
	{"1","0","1-0_1.wav" },
	{"1","0","1-0_2.wav" },
	{"1","0","1-0_3wb.wav" },
	// {"1","1","1-0_3wb.wav" },
	{"2","0","2-0_1.wav" },
	{"2","0","2-0_2.wav" },
	{"2","0","2-0_3.wav" },
	{"2","0","2-0_4.wav" },
	{"2","0","2-0_5.wav" },
	{"2","0","2-0_6.wav" },
	{"2","0","2-0_7.wav" },

	{"3","0","2-1_1.wav" },
	{"3","0","2-1_4.wav" },
	{"3","0","2-1_5.wav" },
	{"3","0","2-1_6.wav" },
	{"3","0","2-1_7.wav" },
	{"3","0","2-1_8.wav" },
	{"3","0","2-1_9.wav" },
	{"3","0","2-1_10.wav" },
	{"3","0","2-1_11.wav" },
	{"3","0","2-1_12.wav" },
	{"3","0","2-1_13.wav" },
	{"3","0","2-1_14.wav" }
};


void setup()
{
	
	// randomiser for speech output, using an unused pin (!)
	randomSeed(analogRead(2));
	Serial.begin(9600);
	music_setup();
	pinMode(pirPin, INPUT);
	pinMode(micPin, INPUT);
	pinMode(ledPin, OUTPUT);
	digitalWrite(ledPin, HIGH);
	delay(1000);
	digitalWrite(ledPin, LOW);
	Serial.println(" Done");
}

bool micHeard = false;
long micLastHeard = 0;
bool lastMicRead = false;

bool pirRead = false;
bool lastPirRead = false;
long pirLastLow = 0;
bool pirEnabled = false;

void loop()
{
	// Ensure someone has gone away before triggering PIR (otherwise convesations can bleed together)
	pirRead = digitalRead(pirPin);
	if (pirRead != lastPirRead) {
		if (!pirRead) {
			pirLastLow = millis();
		}
		lastPirRead = pirRead;
	}

	if (pirRead && (millis() - pirLastLow) > 5000 && pirLastLow != 0) {
		Serial.println("PIR ENABLED");
		pirLastLow = 0;
		pirEnabled = true;
	}

	pirEnabled = true;
	byte micRead = 0;
	if (pirEnabled) {
		pirTriggered = digitalRead(pirPin);
		micRead = !digitalRead(micPin);
	
	// Debounce our mic so have to hear silence for a second...
	if (micRead != lastMicRead) {
		if (micRead) {
			micLastHeard = millis();
		}
		lastMicRead = micRead;
	}

	if (!micRead && (millis() - micLastHeard) > 875 && micLastHeard != 0) {
		Serial.println("MIC TRIGGER");
		micLastHeard = 0;
		micTriggered = true;
	}


	// Assess triggers depending on state:
	// if Heard something, didnt see anyone, and not talking - do nothing
	if (micTriggered && !talking) {
		micTriggered = false;
		triggered = false;
	}

	// If saw someone and not talking - start a conversation
	if (pirTriggered && !talking) {
		triggered = true;
		pirTriggered = false;
		micTriggered = false;
	}

	// if heard someone and already talking - continue conversation
	if (micTriggered && talking) {
		triggered = true;
		micTriggered = false;
		pirTriggered = false;
	}



	// playtime
	if (triggered)
	{
		if (!talking) { // See if a new conversation
			conversationStartTime = millis();
			talking = true;
			Serial.print("Now Talking\t");
		}
		// Play next in current conversation
		playFullSample(playlist[currentTrack][2], playlist[currentTrack][1]);
		Serial.print("Playing Track:");
		Serial.print(playlist[currentTrack][2]);
		// Move On.....
		currentTrack = (currentTrack + 1) % 22;
		Serial.print("\tNextId:");
		Serial.println(currentTrack);

		// set talking = false at end of convo.... if we have reached it
		if (currentPlaylist != playlist[currentTrack][0]) {
			talking = false;
			Serial.print("Last in Conversation");
			pirEnabled = false;
		}
		currentPlaylist = playlist[currentTrack][0];		

		triggered = false;

	}
	}
	else {
		// If Im left alone too long I give up....
		if ((millis() - conversationStartTime) > conversationQuitUs) {
			talking = false;
			triggered = false;
			pirEnabled = false;
			if (currentPlaylist == "1") {
				currentPlaylist = "2";
			} else if (currentPlaylist == "2") {
				currentPlaylist = "3";
			}
			else if(currentPlaylist == "3") {
				currentPlaylist = "1";
			}
			
			currentPlaylist = "1";
			currentTrack = 0;
		}
	}
	
}


// Setup for Music 
void music_setup() {
	// see if the card is inserted and found
	if (!SD.begin(SD_CARD))
	{
		return;   // It's not there, don't do anything
	}
	Serial.println("SD card started");
	// speaker pin output 
	sample.speakerPin = 9;
	// quite loud
	sample.volume(9);
	sample.quality(1);
	sample.disable();
}

// This method plays files of any length....
	// See docs https://github.com/TMRh20/TMRpcm/wiki
// Play whole track but ensure that we flash our LED with the speaker PWM
// and can adjust to get what "feels" better to see
void playFullSample(char* sampleName) {
	playFullSample(sampleName, false);
}

void playFullSample(char* sampleName, char* stopOnMicTrigger) {
	sample.play(sampleName);
	boolean micHeard = false;
	while (sample.isPlaying() && !micHeard) {

		// never actually used this to detect the bang....
		if (stopOnMicTrigger == "1") {
			if (digitalRead(micPin) == HIGH) {
				micHeard = true;
			}
		}
		
		int duration = pulseIn(speakerInputPin, 500); // Allow 1ms before quitting
		int brightness = map(duration, 0, 15, 0, 255);
		if (brightness < 200) { // flatten lower values to 0, so visible changes in brighness
			brightness = 0;
		}
		//Serial.println(duration);
		analogWrite(ledPin, brightness);
		delay(brightness);
	}
	analogWrite(ledPin, 0);
	sample.disable();
}

Credits

arduinocc
2 projects • 13 followers
Contact

Comments

Please log in or sign up to comment.