I started this project a while ago and figured I would share some of what I have so far. It's based on a PIC12f1501 and is pretty simple in practice. The PIC simply counts the amount of clock pulses between latch signals and is able to control the motor from there. 8 clock pulses are ignored, 10 clock pulses disables the motor and 12 clock pulses enables the motor. Simple as that! Unfortunately this means the controller won't work with the four score or satellite... It also has a potentiometer that sets the vibration strength via PWM. The controller and test rom have only been tested on an nes front loader.
Included in the .zip file are some pictures of the prototype pcb and some oscilloscope images while the motor is running to show there is some voltage interference on those lines. There is also my controller test rom and some code which was added to Excitebike to demonstrate how to add rumble to an existing game. I included a screenshot of an Excitebike graphic hack I'm working on (and yes, the speed and temp meters are fully functional!)
Let me know if this is something developers would be interested in for future games. I'd be happy to put together some mod kits once the design is fleshed out!
What's the voltage regulator doing there?
(Or is it a transistor in a beefy package to drive the motors?)
Just a beefy transistor to control the motors.
How much current do those motors want?
The motors are rated at 90 milliamps each. I know that transistor package is unnecessarily large for the job but it was what I had at the time. Here are a few current measurements I took.
Controller w/ ammeter between white wire and controller port:
motors on ~ 185mA
motors off ~ 1.82mA
7805 w/ ammeter between 5v leg and pcb (NES-CPU-10):
motors on ~ 510mA
motors off ~ 310mA
Interesting! I was thinking about it yesterday.
Will take a look.
emerson wrote:
7805 w/ ammeter between 5v leg and pcb (NES-CPU-10):
motors on ~ 510mA
motors off ~ 310mA
Huh, that's kinda big. Do you see or hear any obvious crosstalk from the power supply into the video/audio signal?
I never noticed anything obvious like static or cpu glitch. I put the scope on the audio and video pins going to the rf box and found about 18mV of interference on the audio line. I use a Commodore 1702 for my development monitor and never turn the volume past 1/3, but an audible hum can be heard if I turn the volume up past 1/2. The video signal appears to be unaffected which is good.
Maybe some capacitors placed in parallel near the motors would help reduce that 18mV?
Standard thing you're "supposed" to do in this case is make a "power" lowpass filter, just an inductor and capacitor, to vaguely isolate the 5V supply for the digital logic inside the NES and the shift register from the power supply used by the motors.
(In practice, getting a large enough inductor is often hard: you may just want a resistor or diode instead)
Another option would be looking for lower-current vibration motors. Apparently there's one company that makes some.
I found that increasing the capacitor I have between 5v and ground to 1000uf helps a lot but still isn't perfect. Interference on the audio channel is gone as well as the audible hum. The data line still has interference on the logic low portion of the signal. I'll try the low pass filter idea and see how it works.
Edit: Lower current motors would be the best place to start
Searching for "low current vibration motor" provides one vendor (Jinlong / K'OTL) who sells a variety of 17-40mA 3V vibration motors, plus some others. Just looking at the lower power options from some other vendors also provides some options: for example Precision Microdrives also has at least one 0.25gee model that they claim can operate on as little as 14mA.
Another thing to try would be gradually changing the PWM duty as you turn the motor on, ramping up the voltage instead of just turning it abruptly on. This should help with inrush current.
I may be interested in using this, also. My game is nowhere close to being done, though.
I've always been a little concerned about putting that much current through the controller port. Like when I was thinking of doing a wireless transceiver, I was thinking of putting a large cap on the board which would be charged through an NTC thermistor to limit the current. Maybe I'm just overly cautious? And that's obviously a completely different power profile, compared to a motor. I haven't really considered the current capacity of Nintendo's cables since I'm usually thinking about the 3rd-party extension cables (that have all 7 wires), maybe they're OK. Speaking of that, if you release this, you might want to caution against using certain types of extension cables (I could maybe ID the worst ones). Some of them are just like 3 tiny strands of copper for the wire, I can't imagine 200mA going comfortably though that..
Will it be possible for a game to control the rumble intensity? I suppose the game could do a very slow PWM by updating it once per frame, but I wonder what that effect would be like.
Sorry to dump a feature request on you, please make it however you want. This is what, at least naively, seems like an ideal setup:
10,11 pulses - disable
12,13 pulses - enable (intensity from potentiometer)
x+0,1 pulses - enable intensity 0
x+2,3 pulses - enable intensity 1
..
x+14,15 pulses - enable intensity 7
x+16,17 pulses - disable
Intensity could be the actual PWM, or could be a value scaled by the potentiometer. Disable at the end being I was thinking would be less likely to leave it in a stuck-on state. Thinking of that because programs that expect a Zapper, or asynchronous serial to USB adapter, will put out an unpredictable amount of clocks. And the clocks are divided by 2 because of the DPCM clock glitch.
If someone sold those controllers for okay price and in decent supply, I'd probably support them, but as mod kits not interested.
edit: May also need a switch on the controllers to disable it, in case some game would erroneusly turn it on.
Did a little shopping around and it seems like I have a few options for low current motors so that's good. I've tried the coin motors before. They're noisy and don't produce enough vibration so I want to stick with cylindrical motors.
I think having the game controlled PWM is a great idea! I have a 1:2 prescaler on the counter of the PIC already so odd numbered clock cycles would be ignored at this time. It is possible to have 1:1 prescaler though so odd clock cycles are possible, but if the clocks would have to be divided by 2 because of the DPCM are they really necessary? I guess programs that don't use DPCM could run faster... I only just started with audio programming and don't know much about the DPCM issues, only that there is an issue with controller reading? I'll study up on it.
I used my calipers and measured the diameter of the wires coming from the motherboard to the controller ports in the console itself. They measure approx. 0.015" diameter or 28awg. I counted 7 strands inside which measure approx. 0.005" diameter or 36awg. Im getting different values for max current of 28awg wire but the lowest I found was 226mA so Im pretty maxed out at the moment.
There are two sources of current limits with wire:
#1 is voltage drop, just I·R. 250mA and 28awg and 4 meters is 0.2V. (Have to account for both +5V and ground lines). Probably not a problem? But you have been having problems, I guess.
#2 is self-heating, I²R÷length. At 250mA and 28awg, that's 13mW/meter (doubled, have to dissipate power on both +5 and ground lines) ... the insulation on a cable is like 2mm thick, so that's still something like 0.06K rise.
... ok, I'm impressed at just how conservative that rule of thumb is.
Besides, all a glitch would do is increase intensity by one step for one frame, something the player is unlikely to notice.
Do the Nintendo 64 Rumble Pak and GameCube vibration feature even have an intensity setting?
No, both the Gamecube controller and Rumble Pak use a single bit to control the vibration motor.
Gamecube controller serial protocol consists of a query of three bytes: [0x40 X Y] where Y=even for rumble off and Y=odd for rumble on. (The controller then replies with a standard report). (Lots of other sources vary on what X and the upper bits of Y are. No-one seems to have tracked down what they do)
Rumble Pak protocol piggybacks on top of the protocol for the Transfer Pak: a single bit is mapped to the upper half of its address space, and writes [0x03 Ah Al <31 bytes of don't care> Z] with the ones bit on in the last byte turn the motor on, and vice versa. Oddly enough, the bit is readable too.
Apparently the Wiimote is also just on/off.
Apparently Nintendo's rumble motors are really low current too, but I've only found one source on the net for GC rumble measurement, 30 mA. I want to confirm that and measure a few controllers (orig, third party, new Smash Edition ones) for potential N64 use. Extension cable ordered.
tepples wrote:
Besides, all a glitch would do is increase intensity by one step for one frame, something the player is unlikely to notice.
Yeah, that's a good point. It makes sense to write to it every frame, and the occasional variation really shouldn't be noticeable. DPCM glitch should be pretty much irrelevant in that case.
When I was talking to kevtris about controlling a rumble motor like this, he suggested using the latch/OUT0 line. It's a good idea too, because games normally only leave it active for like 6 CPU cycles to latch the controller. You could also accept CLK pulses while OUT0 is active for sending those parameters. Normal games would pretty much never do that either, because it would only read the A button repeatedly.
Anyways, I'm fine with any control method, just figured I'd put that out there.
I finally have some time to post here
Memblers wrote:
When I was talking to kevtris about controlling a rumble motor like this, he suggested using the latch/OUT0 line.
Yep, I think that's what I would do.
One idea could be to use RS-232 with the OUT0 line.
The routine below works for me with both NTSC and PAL NES (front loaders), but it may be time consuming ...
The receiver must be set to 115400 bauds (not 115200).
If only 4 bits are used, we can reduce the routine, and set the RS-232 receiver to 5N1 or maybe 4N1 ?
Let me know what you think.
Code:
; ZEROPAGE vars
temp: .res 1
rumbleState: .res 1
; ----bbaa
; aa : player 1 motor intensity (0 : stop / 1 : low / 2 : mid / 3 : high)
; bb : player 2 motor intensity (0 : stop / 1 : low / 2 : mid / 3 : high)
; ---- : unused bits ?
lda rumbleState ; 2
sta temp ; 2
lda #0 ; 2
sta $4016 ; 4 ; start bit
lda temp ; 3
nop ; 2
;nop ; 2 ; NTSC - works for me without this line on an NTSC and PAL NES front loader
pha ; 3
and #$01 ; 2
sta $4016 ; 4 - 16 ; bit 0
pla ; 4
lsr ; 2
sta temp ; 3
and #$01 ; 2
sta $4016 ; 4 - 15 ; bit 1
lda temp ; 3
lsr ; 2
nop ; 2
pha ; 3
and #$01 ; 2
sta $4016 ; 4 - 16 ; bit 2
pla ; 4
lsr ; 2
sta temp ; 3
and #$01 ; 2
sta $4016 ; 4 - 15 ; bit 3
lda temp ; 3
lsr ; 2
nop ; 2
pha ; 3
and #$01 ; 2
sta $4016 ; 4 - 16 ; bit 4
pla ; 4
lsr ; 2
sta temp ; 3
and #$01 ; 2
sta $4016 ; 4 - 15 ; bit 5
lda temp ; 3
lsr ; 2
nop ; 2
pha ; 3
and #$01 ; 2
sta $4016 ; 4 - 16 ; bit 6
pla ; 4
lsr ; 2
sta temp ; 3
and #$01 ; 2
sta $4016 ; 4 - 15 ; bit 7
lda temp ; 3
lsr ; 2
nop ; 2
lda temp ; 3
lda #1 ; 2
sta $4016 ; 4 - 16 ; stop bit
Memblers wrote:
When I was talking to kevtris about controlling a rumble motor like this, he suggested using the latch/OUT0 line.
So sending the control byte via the latch line instead of counting clock cycles? That would keep timing equal regardless of the information being sent. The rs232 isn't a bad idea either but won't work with my current prototype. I feel since the clock and latch lines are both present on the microcontroller that simply bit-banging the control bytes in would be sufficient. I can utilize the rs232 code for some of my other projects so thank you glutock for posting that!
One advantage of the rs232 solution is that dpcm glitch won't be a problem.
As long as the bitrate you use is slow enough that losing 4 cycles when a DPCM fetch steals the CPU, that'll be fine.
Other self-clocking encodings may work well, too.
I came up with a new control scheme last night for the rumble controller. Now (in theory) you will be able to do the following:
-independently control the vibration strength each motor in the controller
-read the current vibration status of each motor
-detect that a rumble controller is present
Being able to control the vibration strength was mentioned before and I thought it was a good idea. I also added the ability to control each motor independently for situations like running into either side of the screen or getting shot at from the side. I'm not sure how effective it will be, but it's there. You can read details below on the new control scheme. The diagrams don't line up perfectly but you'll get the idea.
I haven't found a cost effective source for low current motors yet. The ones from precision microdrives will work but are fairly expensive and not much cheaper in bulk, but that might just be what it is.
Code:
;*****This document was written in Notepad++
;----------------------------------------------------------------
;WRITING TO THE MOTORS
;
; Each rumble controller has two motors that can be
; controlled independently with a four bit value. This value
; should be sent to the controller MSB first and is read by
; the controller on the low clock pulse. Writing a non-zero
; value to the motors will enable them, and writing zero
; to the motors will turn them off. The value written to
; the motors correlates to the strength of vibration with
; '1111' being the strongest vibration. Set the respective
; controller write flag to initiate a write cycle.
;
;
;
;
; Vibration strength bit assignment:
;
; p(#)_motors = 0000 0000
; ││││ ││││
; ││││ │││└──right motor bit 0
; ││││ ││└───right motor bit 1
; ││││ │└────right motor bit 2
; ││││ └─────right motor bit 3
; ││││
; │││└───────left motor bit 0
; ││└────────left motor bit 1
; │└─────────left motor bit 2
; └──────────left motor bit 3
;
;
;
;
; Write flags bit assignment:
;
; motor_flags = xxxx xx00
; ││
; │└──write controller 1
; └───write controller 2
;
;
;
;
; Write cycle timing diagram:
;
; 0 1 2 3 4 5 6 7 8
; ─────┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌────
; │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
; clock └───┘ └───┘ └───┘ └───┘ └───┘ └───┘ └───┘ └───┘ └───┘
; ┌──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┐
; │ WREN │ M1.3 │ M1.2 │ M1.1 │ M1.0 │ M2.3 │ M2.2 │ M2.1 │ M2.0 │
; latch ───┘ └──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┴───
;
;
;
;
; Controller 1 write example:
;
; Cont1Write:
; ;-----check if controller write flag is set-----
; lda motor_flags ;load motor flags
; and #$01 ;isolate controller 1 write flag
; beq + ;skip if not set
;
; ;-----send write latch pulse-----
; lda #$01 ;set latch line high
; sta $4016 ;write to port
; ldy #$06 ;wait for latch pulse to rise (unnecessary?)
; -
; dey
; bne -
; lda $4016 ;send clock pulse to controller while latch is high to indicate this is a write operation
; ldx #$08 ;set write loop counter
;
; ;-----write motor data-----
; lda p1_motors ;load motor values
; sta motor_write ;and store in scratch variable
; --
; asl motor_write ;shift bit into carry
; rol a ;roll bit into accumulator
; and #$01 ;mask all other bits
; sta $4016 ;write bit to latch line
; ldy #$06 ;wait for latch pulse to rise (unnecessary?)
; -
; dey
; bne -
; lda $4016 ;clock the latch bit into controller
; dex ;decrement loop counter
; bne --
;
; ;-----clear latch line-----
; lda #$00 ;return latch to
; sta $4016 ;normal state
;
; ;-----erase the write flag-----
; lda motor_flags ;load motor flags
; and #$fe ;erase controller 1 write flag
; sta motor_flags ;
; +
; rts
;
;
;
;READING CURRENT MOTOR STATUS
;
; The status of each motor in the rumble controller can
; be read by clocking in an additional 8 bits after reading
; the button status of the controller. The motor status
; byte is sent to the NES with the MSB being the first bit.
; Reading a non-zero condition correlates to the current
; vibration strength while reading a zero means the motors
; are off. Set the respective controller read flag to
; initiate a read cycle.
;
;
;
;
; Motor status bit assignment:
;
; motor_read = 0000 0000
; ││││ ││││
; ││││ │││└──right motor bit 0
; ││││ ││└───right motor bit 1
; ││││ │└────right motor bit 2
; ││││ └─────right motor bit 3
; ││││
; │││└───────left motor bit 0
; ││└────────left motor bit 1
; │└─────────left motor bit 2
; └──────────left motor bit 3
;
;
;
;
; Read flags bit assignment:
;
; motor_flags = xxxx 00xx
; ││
; │└──read controller 1
; └───read controller 2
;
;
;
;
; Read cycle timing diagram:
;
; 0 1 2 3 4 5 6 7 8 9 A B C D E F
; ─────┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌────
; │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
; clock └───┘ └───┘ └───┘ └───┘ └───┘ └───┘ └───┘ └───┘ └───┘ └───┘ └───┘ └───┘ └───┘ └───┘ └───┘ └───┘
; ┌┐
; ││
; latch ───┘└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────
; ┌──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┐
; │ A │ B │ SE │ ST │ U │ D │ L │ R │ M1.3 │ M1.2 │ M1.1 │ M1.0 │ M2.3 │ M2.2 │ M2.1 │ M2.0 │
; data ───┴──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┴───
;
;
;
; Controller 1 read example:
;
; Cont1Read:
; ;-----send read latch pulse-----
; lda #$01 ;set latch line high
; sta $4016 ;write to port
; lda #$00 ;clear latch line before first clock pulse
; sta $4016 ;to indicate this is a read operation
; ldx #$08 ;set button read loop counter
;
; ;-----read controller 1 data-----
; -
; lda $4016 ;load data from controller port 1
; lsr a ;shift bit 0 into carry
; rol cont1 ;and store carry in bit 0 of cont1 variable
; dex ;decrement x
; bne - ;repeat 8 times for all buttons
;
; ;-----check if controller 1 motor read flag is set-----
; lda motor_flags ;load motor flags
; and #$04 ;isolate controller 1 read flag
; beq + ;skip if not set
;
; ;-----read controller 1 motor status-----
; ldx #$08 ;set motor status loop counter
; -
; lda $4016 ;load data from controller port 1
; lsr a ;shift bit 0 into carry
; rol p1_status ;and shift carry in bit 0 of motor status byte
; dex ;decrement x
; bne - ;repeat 8 times for both motors
;
; ;-----erase the read flag-----
; lda motor_flags ;load motor flags
; and #$fb ;erase controller 1 read flag
; sta motor_flags ;
; + ;skip to here
; rts
;
;
;
;DETECTING THE RUMBLE CONTROLLER
;
; On power-up, the rumble controller motor status will
; read '11011011' even though the motors are off. This enables
; you to detect whether the controller has rumble or not.
; The motor status byte will be cleared after the first read.
; To detect rumble controllers, a read cycle should be initiated
; after the reset code and before your main game code.
; The corresponding detect bit in motor_flags will be set
; if no rumble controller is detected.
;
;
;
;
; Detect flags bit assignment:
;
; motor_flags = 00xx xxxx
; ││
; │└──controller 1 rumble detect (0=rumble present 1=no rumble)
; └───controller 2 rumble detect (0=rumble present 1=no rumble)
;
;
;
;
; Detect sequence example:
;
; ;-----check if controller 1 is a rumble controller-----
; lda #$04 ;load controller 1 read flag
; sta motor_flags ;write to call a read cycle
; jsr ReadCont1 ;read data from controller 1
; lda p1_status ;load detect byte
; cmp #$db ;compare to rumble present byte
; beq + ;skip if rumble controller is present
; lda #$40 ;load no rumble flag
; ora motor_flags ;logical or with current motor_flags
; sta motor_flags ;write no rumble flag
; +
; lda #$00 ;clear status byte
; sta p1_status
;
;----------------------------------------------------------------