PICAXE Speed Controller
with MP3 Player

d. bodnar   revised 11-23-2015

 

Introduction
The PICAXE Speed Controller that is described here
http://www.trainelectronics.com/PicaxeSpeedController/article.htm has been updated so that the same infrared controller that is used to change speed, direction and other functions can now access sound files on a small MP3 player.

 

Playing Sound Files
Sound files are stored on a micro SD card that is inserted into the DFPlayer.  The files are stored in a subdirectory called mp3 and named 0001.mp3, 0002,mp3 and so on.  The current software revision can address up to 255 sound files.

To play a sound file press the "Source" button on the TV remote control until the blue LED illuminates (it may take more than one press).  Once the LED is lit enter the three digit file number.  For single or double digit file names you MUST enter leading zeros.  For example, to play file number 7 you must enter 007 on the keypad.

Schematic
The schematic has been modified to accommodate the DFPlayer.  Pins 18 is used to send commands to the DFPlayer and pin 17 is used to sense when a sound file is being played.

Hardware
The DFPlayer is mounted on a secondary board and connects to the main PICAXE board with four wires, +5 volts, ground, data and busy.

The wiring on the back of the DFPlayer is shown here - note the 1K resistor that goes to the RX pin.

The yellow and orange wires connect as shown here on the front of the PICAXE board.

+5 volts and ground can be picked up as shown.

Acrylic Plastic Case
This case was cut from 1/8" acrylic using a laser cutter.  It has cutouts for the heat sink, power plug, track connection and speaker.

 

Software
The code below is the original program plus the MP3 player code.
 
'added DFPlayer code 11-23-2015
'd. bodnar - 11-02-2011  v6.3  
' note about direction added 2-24-12
#PICAXE 18M2
#TERMINAL 9600
#no_data
SETFREQ m8 	' needed to get baud to work with DFPlayer 
Symbol Direction2	= b.0	'pin 6 - one high & one low - swap to change direction
'Symbol NOTUSEDb1  = b.1 'pin 7
Symbol IRSense 	= b.2	'pin 8
Symbol Motor 	= b.3	'pin 9 - PWM output for motor speed
'Symbol NOTUSEDb4  = b.4 'pin 10
Symbol Direction1	= b.5	'pin 11 - one high & one low - swap to change direction-if = fast motor stop
'Note - change above from pin 11 to pin 7 - b.1 for older circuit board
'Symbol NOTUSEDb6  = b.6 'pin 12
'Symbol NOTUSEDb7  = b.7 'pin 13

Symbol MP3Busy	= c.0	'pin 17 - TEST FOR Busy from DFPlayer
Symbol MP3Tx  	= pinc.1	'pin 18 - TEST FOR TX to DFPlayer
'Symbol Aux1		= c.2 'pin 1 - can be used for current sensing - OR OTHER PURPOSES!
'Symbol NOTUSEDc3  = c.3 'pin 2
'Symbol NOTUSEDc4  = c.4 'pin 3
'Symbol Aux4		= c.5 'pin 4  input only
Symbol LEDAmber 	= c.6	'pin 15
Symbol LEDBlue	= c.7	'pin 16
'Symbol HalfAC	= pinb.4 'pin 10

Symbol InfraRED	= b27 'store IR value 
symbol Digit	= b26 'Flash Value
symbol Value 	= b25 'Flash Value & Version
Symbol ValueTemp  = b24 'Flash Value
symbol divisor	= b23 'Flash Value
symbol temp5	= b22 'Flash Value & GetRndOnce 
symbol temp		= b21 'Flash Value & Version
Symbol CruiseTime	= b20 'Time to run before reversing
symbol digitsbyte = b19 'Get number routine
Symbol TempByte	= b18	'Flash Value & Version & Get IR Num
Symbol WaitTime	= b17	'pause before reverse
Symbol ADRate	= b16	'accel / decel rate
Symbol DecelTime	= b15 'point where decel beings on p-2-p
Symbol RndmWait	= b14 'RANDOM - needed?
Symbol mp3file    = b13 
Symbol temp2	= b12
Symbol UnUsedb11	= b11
Symbol TempWord	= w5  'RANDOM - needed?
Symbol Loopie0    = w4	'Decel & Random
Symbol MinSpeed	= w3
Symbol SetSpeed	= w2
Symbol DecelFlag	= Bit0 '=1 if decel time has caused deceleration otherwise =0
Symbol FwdBkwFlag = Bit1 '=1 if forwrad / =0 if backward
Symbol SlowDnFlag = Bit2 '=0 if no slow down point set, =1 if set
Symbol Decel2Zero = Bit3 '=1 if we want deceleration to zero, not minimum speed
'Also Using 
	'Memory 30 for Decel loop start value (word)
	'Memory 32 for Decel loop end value (word)
	'Memory 34 for time temp storage (byte)

Symbol LStart 	= 0'120'450
Symbol TTrigger 	= 7000
Symbol Period 	= 150
Symbol PWMMax 	= 1023'600
Symbol ChUp		= 16
Symbol ChDown	= 17
Symbol VolUp	= 18
Symbol VolDown	= 19
Symbol OK		= 20
Symbol Retrn	= 59
Symbol One		= 0
Symbol Two		= 1
Symbol Three	= 2
Symbol Four		= 3
Symbol Five		= 4
Symbol Six		= 5
Symbol Seven	= 6
Symbol Eight	= 7
Symbol Nine 	= 8
Symbol Zero		= 9
Symbol Power	= 21
Symbol MENU		= 96
Symbol VChip	= 14
Symbol Exxit	= 99
Symbol VerWhole 	= 6
Symbol VerDecimal = 3
Symbol Source = 37 ' source button used to initiate MP3 sound from DFPlayer
high direction1:low direction2

VeryTop:
Read 0, WORD SetSpeed	'read variables from memory
Read 2, WORD MinSpeed
Read 4, WaitTime
Read 5, ADRate
Read 6, DecelTime
Read 7, RndmWait
Read 8, slowdnflag
if DecelTime=0 then 
	DecelTime=WaitTime
endif
decelflag=0
if deceltime <> waittime then
	decelflag=1
	slowdnflag=1
endif

pwmout motor,50,500' set pwm duty
pwmduty motor, 0
Pause 500
sertxd("d. bodnar",13,10,"11-23-2015 v6.3- DFPlayer 1.1",13,10,13,10) '9600 baud @ this clock speed


;Routine to flash out version number of software
for tempbyte=1 to VerWhole:high LEDBlue:pause 150:low LEDBlue:pause 150:next tempbyte:pause 400
temp=verdecimal
IF temp > 0 then
	for tempbyte=1 to VerDecimal:high LEDBlue:pause 150:low LEDBlue:pause 150:next tempbyte
	else
	high ledblue:pause 10:low ledblue
endif
Pause 1000

GOSUB ShowReadValues
gosub accel
time=0

Cruise:
Random TempWord
if time <=2 then gosub GetRndOnce

'Routine to show time that has passed on Terminal
PEEK 34, TempByte
if time>254 then 
	time=0
endif
if time <> TempByte then
	sertxd("t=",#time," ") 
	POKE 34, time
	toggle ledamber
endif

'if no auto-reverse (waittime) has been set don't reverse or decel 
if waittime=0 then 
	goto SkipReverse
endif

'Slow down to minspeed if decel point has been set - do it only once via decelflag 
if time >= DecelTime and decelflag =0 and DecelTime<>CruiseTime then
	sertxd("@deceltime ",#deceltime," f ",#decelflag,13,10)
	gosub DecelSet2Min
	DecelFlag=1
endif

'Run time has expired - bring to complete stop & reverse
if time >=CruiseTime then
	sertxd("Time= ",#time,13,10)
	time=0
	decelflag=0
	goto Revers
endif

SkipReverse:
'Get IR input and act on it 
{Infrared=255
Irin [1, Cruise],IrSense, InfraRED
sertxd("IR  0= ",#infrared," ")

if InfraRed = Source then
	sertxd(10,13,"@ Source Button ",10,13)
   gosub getirnumber
   sertxd("mp3 # = ",#digitsbyte," ")
	   
   MP3File = digitsbyte
   gosub pmp3
endif

if InfraRED = Power then	'use POWER button to set deleration point
	DecelTime=time
	SlowDnFlag=1
	Write 6, DecelTime
	Write 8, Slowdnflag
	gosub ShowReadValues
endif

if InfraRED = OK then 		'Mute on Westinghouse Remote - hit it to set minimum speed
	MinSpeed=SetSpeed
	Write 2, word MinSpeed
	Write 8, Slowdnflag
	gosub ShowReadValues
endif


if InfraRED = Retrn then GetOption	'after RETURN button choose 1-->4 to set options

if InfraRED = VolUp and FwdBkwFlag =0 then ' VOLUME UP and DOWN to change direction
	FwdBkwFlag = 1
	goto revers
endif

if InfraRED=VolDown and FwdBkwFlag =1 then ' VOLUME UP and DOWN to change direction
	FwdBkwFlag = 0
	goto revers
	endif

if InfraRed = MENU then	'press MENU to pause & save speed or to restart after pausing
Sertxd("@ MENU",13,10)
	if SetSpeed > 0 then
		POKE 30,WORD SetSpeed
		if MinSpeed <> 0 then 
			Decel2Zero=1
		endif
		gosub decel
		SetSpeed=0
		disabletime
		pause 1000
	else
		PEEK 30, WORD SetSpeed
		gosub accel
		enabletime
	endif
endif

if InfraRED=ChUp then  'CHANNEL UP and DOWN to speed up or down
	high LEDBlue
	SetSpeed = SetSpeed +5
	pwmduty motor,SetSpeed
	write 0, WORD SetSpeed
	Write 8, Slowdnflag
	sertxd(#setspeed,13,10)
	else
	low LEDBlue
endif

if InfraRED=ChDown then  'CHANNEL UP and DOWN to speed up or down
	high LEDBlue
	if SetSpeed > 5 then
	SetSpeed = SetSpeed -5
	sertxd(#setspeed,13,10)
	endif
	pwmduty motor,SetSpeed
	write 0, WORD SetSpeed
	Write 8, Slowdnflag	
	else
	low LEDBlue
endif}
goto Cruise:

{
GetRndOnce:   'Put time to run train into Temp
Random TempWord
loopie0=65535/rndmwait
Temp5=tempword/loopie0
CruiseTime=WaitTime+temp5
return

GetOption:
pwmduty motor,0 ; STOP
sertxd("@GetO",13,10)
pause 1000
StayHere2: 
Infrared=255
Irin [1, StayHere2],IrSense, InfraRED
sertxd("IR= ",#infrared," ")
if infrared = VChip then 
	goto ReadyReset:
endif
if infrared >4 then	'only accepts number buttons 1-->4
 goto cruise
endif

Branch infrared, (speedmax, speedmin, runtime, acceldecelrate, RndmSetting)
goto cruise

RndmSetting:
value = RndmWait
gosub flashvalue
gosub getirnumber
RndmWait=digitsbyte
sertxd("RndmWait= ",#RndmWait,13,10)
write 7, RndmWait
Write 8, Slowdnflag
gosub ShowReadValues
gosub accel
goto cruise:

speedmax:
value = setspeed
gosub flashvalue
gosub getirnumber
setspeed=digitsbyte
sertxd("max= ",#setspeed,13,10)
write 0, word setspeed
Write 8, Slowdnflag
gosub ShowReadValues
gosub accel
goto cruise:

speedmin:
value = minspeed
gosub flashvalue
gosub getirnumber
minspeed=digitsbyte
sertxd("min= ",#minspeed,13,10)
write 2, word MinSpeed
gosub ShowReadValues
gosub accel
goto cruise:

runtime:
value = waittime
gosub flashvalue
gosub getirnumber
waittime=digitsbyte
write 4, WaitTime
Write 8, Slowdnflag
sertxd("wait= ",#waittime,13,10)
time=0
if DecelTime=0 then 
	DecelTime=WaitTime
endif
gosub ShowReadValues
gosub accel
goto cruise:

acceldecelrate:
value = adrate
gosub flashvalue
gosub getirnumber
adrate=digitsbyte
write 5, adrate
Write 8, Slowdnflag
gosub ShowReadValues
sertxd("ad= ",#adrate,13,10)
goto cruise:

ReadyReset:
Infrared=255
Irin [1, ReadyReset],IrSense, InfraRED
if infrared = Exxit then DoIt
if infrared = Power then Cruise
goto readyreset
DoIt:
deceltime=0:setspeed=0:minspeed=0:waittime=0:adrate=0:RndmWait=0:slowdnflag=0
Write 0, WORD SetSpeed
Write 2, WORD MinSpeed
Write 4, WaitTime
Write 5, ADRate
Write 6, DecelTime
Write 7, RndmWait
Write 8, slowdnflag
goto VeryTop:

AutoReverse:
for b0=1 to 10:toggle IrSense:pause 100:next b0
goto cruise}

Revers:
sertxd("@Revers",13,10)
Decel2Zero=1
gosub decel 
pause 1000
toggle direction1:toggle direction2
gosub accel
goto Cruise:

Accel:
disabletime
sertxd("@Accel",13,10)
for loopie0= 0 to SetSpeed step 5
sertxd(#loopie0," ")
pwmduty motor,loopie0 ; set pwm duty
toggle ledblue
pause adrate
next loopie0
sertxd(13,10)
enabletime
return

Decel:
disabletime
sertxd("@Decel",13,10)
if decelflag=1 then 
	return
endif
if SlowDnFlag=1 then
	for loopie0=minspeed to 0 step -3
		gosub DecelLoop:
	next loopie0
	goto skip0:
endif 
if Decel2Zero=1 then 
	Decel2Zero=0
	for loopie0=setspeed to 0 step-3	
		gosub DecelLoop:
	next loopie0
	goto skip0:
endif 
DecelSet2Min:
sertxd("@DecelSet2Min",13,10)
for loopie0= SetSpeed to MinSpeed step -5
	gosub DecelLoop:
next loopie0
Skip0:
sertxd(13,10)
enabletime
low LEDBlue
return

DecelLoop:
toggle LEDBlue
sertxd(#loopie0," ")
pwmduty motor, loopie0
pause adrate
return

FlashValue:	'Flash out current reading
sertxd(13,10,"@FV ",#value,13,10)
ValueTemp=Value
for TempByte= 2 to 0 step -1
  if TempByte=2 then 
	divisor=100
  endif
  if TempByte=1 then 
	divisor=10
  endif	
  if TempByte=0 then 
	divisor=1
  endif

 Digit=ValueTemp /divisor
 sertxd("digit ",#digit," ",13,10)' debug Digit
 temp5=Digit * divisor
 ValueTemp=ValueTemp - temp5
 if Digit=0 then 
    high LEDBlue:pause 50:low LEDBlue:pause 100:pause 600:goto skipover3:
 endif             
  for temp=1 to Digit
    high LEDBlue:pause 200:low LEDBlue:pause 200
  next temp
  pause 600    
  SkipOver3:
next TempByte
return

GetIRNumber:
sertxd(13,10,"@GetIR 2",13,10)
DigitsByte=0:digit=3
GetDigits0:
InfraRED=0
'pause 40
irin [10, GetDigits0],irsense, InfraRED  'Irin [1, StayHere2],IrSense, InfraRED
'pause 20
if infrared=37 then
	high  ledblue
   sertxd ("3--7",10,13)
   goto getdigits0	
endif
 high ledblue
sertxd("@Blue LED!!!!!!!!!!!!",13,10)
high ledblue
sertxd("IRx ",#InfraRED," ",13,10)
if infrared > 9 then 
return 
endif
infrared =infrared +1
if infrared = 10 then 
	infrared=0 
endif
if digit=3 and infrared>2 then getdigits0:'can't be above 2hundreds
if digit=2 and digitsbyte = 2 and infrared > 5 then getdigits0
if digit=1 and digitsbyte =25 and infrared > 5 then getdigits0
skiprnd:
sertxd("Digit= ",#digit,13,10)
if infrared=0 then 
	high LEDBlue:pause 50:low LEDBlue 
endif
for tempbyte=1 to infrared:high LEDBlue:pause 100:low LEDBlue:pause 100:next tempbyte
DigitsByte=DigitsByte*10+infrared
digit=digit-1
pause 200':'command=0
if digit=0 then 
	sertxd("Digitsbyte ",#digitsbyte," ",13,10)
      return
endif
goto GetDigits0:

ShowReadValues:
SERTXD("SPD, MIN, WAIT, AD, RdomWait,DecelFlag ",#setspeed," ",#minspeed," ",#waittime, " ", #adrate," ",#RndmWait," ",#deceltime," ",#b0, 13,10)
sertxd ("sdf=",#slowdnflag,13,10)
RETURN

PMP3:
sertxd(13,10,"@PMP3 ",#mp3file,13,10)
temp = mp3file+1
temp5=$e9-temp
serout c.1 ,T9600_8,( $20,$20,$7e,$ff,$06,$12,0,0,temp,$fe, temp5, $ef ,$20,$20)    'Play 1
return