Strobe Randomization

Update 6/21/2016: I’ve added a binary build below!

Last year in a group buy we bought some LED strobe lights. They self-randomize, but after testing them, we found out they don’t randomize for quite some time. When they first turn on, they all flash in unison and slowly change based on the random delay length.

This wouldn’t fit well with what we were planning for our strobes (short use at certain features parts of songs) so I had to come up with something. I didn’t want to use left-over Renard channels because of things like $/channel and lack of controllers in certain areas. For example, our mega tree has 16 strands, next year we’ll have a star, and we want 10-12 strobes. Instead of using more than 1 renard controller (to have a strobe per channel for the random start we want) I came up with a cheaper solution. Albeit, not much cheaper, I was able to do some learnin’ which was also a motivating factor for me to go the selected route.

DirkCheap SSR

Most 4-channel SSRs circuits in the DIY community cost around $8 each when purchasing a few through a group buy. An SSR is something that switches high voltage loads for your lights. They’re typically grouped by 4 channels and use a Cat5 cable with RJ45 connectors to control the individual channels. They’re typically driven by a Renard, DMX, or other controler that doesn’t have onboard SSRs. Part of the cost of most typical SSR circuits is the triac which with the right part, can handle quite a lot of current. The DirkCheap SSRs don’t use a triac because they use an optoisolator that can handle up to 1A of a 120V load. This eliminates one of the more expensive parts of an SSR and the cost of a DirkCheap is along the lines of $4 per SSR when purchasing a few through a group buy. The stick is, you can only switch 1A or less rather than a typical SSR which can be a couple to a few amps per channel. In my case the strobes are not even near an amp.

DIGWDF 675

Instead of using individual channels and sequencing software to randomize each strobe, I figured I’d try working something out to use only 1 channel. This would make it much easier in sequencing to turn on 1 channel for strobes, instead of multiple. This lead me to the DIGWDF675 which was designed to run some sort of pre-determined sequence for 4 channels when powered with 5V. This means you can somewhat multiplex channels. With the help of teh tubes I learned enough PIC assembly to be dangerous, as well as accomplish my task. I adapted code for a few things. I wrote subroutines for randomization (mostly from an example on the web). I wrote code to take the least significant 4 bits as flags for 4 different outputs. I wrote code to delay 10ms (largely adapted from examples on the web). Lastly I wrote the main firmware which read the PIC12F675’s internal EEPROM to seed the random value generator, wrote the next generated value back to EEPROM, then randomly turned on output pins. This meant that the LEDs would all come on at slightly different times and in random order. That combined with the LED’s on-board randomization gives the effect we want.

Here’s the code that is used:

firmware.asm
;******************************************************************************
;                                                                             *
;    Filename:      firmware.asm                                              *
;    Date:          2012/11/14                                                *
;                                                                             *
;    Author:        Mathew Mrosko                                             *
;                                                                             *
;******************************************************************************
;                                                                             *
;    Files required: P12F675.INC                                              *
;                                                                             *
;******************************************************************************
;                                                                             *
;    Notes:         Main firmware file to wrap functionality.                 *
;                   Pseudo code of what this does:                            *
;                       * Sets up PIC12F675                                   *
;                       * Reads EEPROM                                        *
;                       * Seeds random generator with EEPROM value            *
;                       * Calls random subroutine                             *
;                       * Writes new random value to EEPROM                   *
;                       * Randomly turns on 1 of 4 output pins                *
;                                                                             *
;******************************************************************************

;------------------------------------------------------------------------------
; PROCESSOR DECLARATION
;------------------------------------------------------------------------------

     LIST      P=12F675              ; list directive to define processor
     #INCLUDE           ; processor specific variable definitions

     ; Functions from other files
     EXTERN     delay10_R
     EXTERN     blink_R
     EXTERN     turn_on_R
     EXTERN     random_R

;------------------------------------------------------------------------------
;
; CONFIGURATION WORD SETUP
;
; The 'CONFIG' directive is used to embed the configuration word within the 
; .asm file. The lables following the directive are located in the respective 
; .inc file.  See the data sheet for additional information on configuration 
; word settings.
;
;------------------------------------------------------------------------------

;    __CONFIG   _CP_OFF & _CPD_OFF & _BODEN_OFF & _MCLRE_OFF & _WDT_OFF & _PWRTE_ON & _INTRC_OSC_NOCLKOUT
    __CONFIG _INTRC_OSC_NOCLKOUT & _WDT_OFF & _PWRTE_ON & _MCLRE_OFF & _CP_OFF & _CPD_OFF

;------------------------------------------------------------------------------
; VARIABLE DEFINITIONS
;------------------------------------------------------------------------------

; example of using Shared Uninitialized Data Section
            UDATA_SHR

sGPIO       RES     1       ; Shadow register for GPIOs
            GLOBAL  sGPIO   ; Make it accessible elsewhere
RANDOM      RES     1
            GLOBAL  RANDOM

d1 res 1
d2 res 1

;------------------------------------------------------------------------------
; RESET VECTOR
;------------------------------------------------------------------------------

RESET   CODE    0x0000  ; processor reset vector
        GOTO    START         ; go to beginning of program

;------------------------------------------------------------------------------
; SUBROUTINE VECTORS
;------------------------------------------------------------------------------
delay10
        ;pagesel delay10_R
        goto    delay10_R
        GLOBAL  delay10 ; Make this global so others can use the vectors

blink
        ;pagesel blink_R
        goto    blink_R

turn_on
        ;pagesel turn_on_R
        goto    turn_on_R

random
        ;pagesel random_R
        goto    random_R

;------------------------------------------------------------------------------
; MAIN PROGRAM
;------------------------------------------------------------------------------

MAIN_PROG   CODE
START

;------------------------------------------------------------------------------
; PLACE USER PROGRAM HERE
;------------------------------------------------------------------------------

        errorlevel -302
        movlw   b'00001100' ; set everything to output (except GP3)
        banksel TRISIO
        movwf   TRISIO

        movlw   b'00000100' ; disable digital input buffer (set AN2 it to analog)
        banksel ANSEL
        movwf   ANSEL

        movlw   b'10001000' ; set right justified and select AN2 to be read into ADRESH
        banksel ADCON0
        movwf   ADCON0

        movlw	b'00000011'	; Comparator configuration
        banksel CMCON
        movwf	CMCON

        ;movlw   b'00111111' ; make them all high (LEDs wired to light when pin is low)
        movlw   b'00000100' ; make them all low (LEDs wired to light when pin is high)
        movwf   GPIO
        movwf   sGPIO
        errorlevel +302

;-----------ADC CODE-----------
;seed_random
;        bsf     ADCON0,GO       ;Start A/D conversion
;read_adc_wait
;        btfsc   ADCON0,GO       ;Wait for conversion to complete
;        goto    read_adc_wait
;        movf    ADRESH,w        ;save analog read to w
;        movwf   RANDOM          ;write A/D result to RANDOM
;-----------ADC CODE-----------

;-----------MANUAL SEED CODE-----------
;        movlw   b'10101110'
;        movwf   RANDOM
;-----------MANUAL SEED CODE-----------

;-----------EEPROM CODE-----------
        ;read random
        errorlevel -302
        movlw   .0
        banksel EEADR
        movwf   EEADR
        bsf     EECON1,RD
        movf    EEDATA,w
        banksel RANDOM
        movwf   RANDOM

        call    random
        movf    RANDOM,w

        ;save random
        banksel EEDATA
        movwf   EEDATA  ;move the random thing (now in W) to EEDATA for the write
        bsf     EECON1,WREN
        bcf     INTCON,GIE  ;disable interrupts
        movlw   055h            ; REQUIRED FOR WRITE
        movwf   EECON2          ; REQUIRED FOR WRITE
        movlw   0AAh            ; REQUIRED FOR WRITE
        movwf   EECON2          ; REQUIRED FOR WRITE
        bsf     EECON1,WR       ; REQUIRED FOR WRITE
        bsf     INTCON,GIE  ;re-enable interrupts
        errorlevel +302
;-----------EEPROM CODE-----------

main_loop
        call Delay
        call Delay
        call Delay
        call Delay
        call Delay
        banksel RANDOM
        call    random
        movf    RANDOM,w
        call    turn_on
        goto    main_loop

; Delay = 0.01 seconds
; Clock frequency = 4 MHz

; Actual delay = 0.01 seconds = 10000 cycles
; Error = 0 %

;	cblock
;	d1
;	d2
;	endc

Delay
			;9993 cycles
	movlw	0xCE
	movwf	d1
	movlw	0x08
	movwf	d2
Delay_0
	decfsz	d1, f
	goto	$+2
	decfsz	d2, f
	goto	Delay_0

			;3 cycles
	goto	$+1
	nop

			;4 cycles (including call)
	return

loop_forever
        goto    loop_forever

        END
turn_on.asm
;******************************************************************************
;                                                                             *
;    Filename:      turn_on.asm                                               *
;    Date:          2012/11/14                                                *
;                                                                             *
;    Author:        Mathew Mrosko                                             *
;                                                                             *
;******************************************************************************
;                                                                             *
;    Files required: P12F675.INC                                              *
;                                                                             *
;******************************************************************************
;                                                                             *
;    Notes:         Turns on one of 4 output pins based on                    *
;                   least significant bits.                                   *
;                                                                             *
;******************************************************************************

;------------------------------------------------------------------------------
; PROCESSOR DECLARATION
;------------------------------------------------------------------------------

#INCLUDE           ; processor specific variable definitions

#define POSITIVE_OUTPUT

;------------------------------------------------------------------------------
; VARIABLE DEFINITIONS
;------------------------------------------------------------------------------

        GLOBAL  turn_on_R
        EXTERN  sGPIO

; example of using Shared Uninitialized Data Section
        UDATA_SHR
led_val RES     1

        CODE

turn_on_R
        andlw   b'00000111'     ;Save the last bits for 0
        movwf   led_val

check_1
        decfsz  led_val,F
        goto    check_2
        goto    turn_on_1
check_2
        decfsz  led_val,F
        goto    check_3
        goto    turn_on_2
check_3
        decfsz  led_val,F
        goto    check_4
        goto    turn_on_3
check_4
        decfsz  led_val,F
        goto    done
        goto    turn_on_4

turn_on_1
        movf    sGPIO,w
        iorlw   b'00100000'
        xorlw   b'00100000'
IFDEF POSITIVE_OUTPUT
        xorlw   b'00100000'
ENDIF
        movwf   sGPIO
        movwf   GPIO
        goto    done
turn_on_2
        movf    sGPIO,w
        iorlw   b'00010000'
        xorlw   b'00010000'
IFDEF POSITIVE_OUTPUT
        xorlw   b'00010000'
ENDIF
        movwf   sGPIO
        movwf   GPIO
        goto    done
turn_on_3
        movf    sGPIO,w
        iorlw   b'00000010'
        xorlw   b'00000010'
IFDEF POSITIVE_OUTPUT
        xorlw   b'00000010'
ENDIF
        movwf   sGPIO
        movwf   GPIO
        goto    done
turn_on_4
        movf    sGPIO,w
        iorlw   b'00000001'
        xorlw   b'00000001'
IFDEF POSITIVE_OUTPUT
        xorlw   b'00000001'
ENDIF
        movwf   sGPIO
        movwf   GPIO
        goto    done
done
        retlw   0

        END
random.asm
;******************************************************************************
;                                                                             *
;    Filename:      random.asm                                                *
;    Date:          2012/11/14                                                *
;                                                                             *
;    Author:        Mathew Mrosko                                             *
;                                                                             *
;******************************************************************************
;                                                                             *
;    Files required: P12F675.INC                                              *
;                                                                             *
;******************************************************************************
;                                                                             *
;    Notes:         Generates a pseudorandom number.                          *
;                                                                             *
;******************************************************************************

;------------------------------------------------------------------------------
; PROCESSOR DECLARATION
;------------------------------------------------------------------------------

#INCLUDE           ; processor specific variable definitions

;------------------------------------------------------------------------------
; VARIABLE DEFINITIONS
;------------------------------------------------------------------------------

        EXTERN  RANDOM
        GLOBAL  random_R

        CODE

;;; Random number generator.
random_R
        RLF     RANDOM,W
        RLF     RANDOM,W
        BTFSC   RANDOM,4
        XORLW   1
        BTFSC   RANDOM,5
        XORLW   1
        BTFSC   RANDOM,3
        XORLW   1
        MOVWF   RANDOM
        RETLW   0

        END
Delay10.asm
;******************************************************************************
;                                                                             *
;    Filename:      xxx.asm                                                   *
;    Date:                                                                    *
;                                                                             *
;    Author:                                                                  *
;                                                                             *
;******************************************************************************
;                                                                             *
;    Files required: P12F675.INC                                              *
;                                                                             *
;******************************************************************************
;                                                                             *
;    Notes:                                                                   *
;                                                                             *
;******************************************************************************

;------------------------------------------------------------------------------
; PROCESSOR DECLARATION
;------------------------------------------------------------------------------

#INCLUDE           ; processor specific variable definitions

;------------------------------------------------------------------------------
; VARIABLE DEFINITIONS
;------------------------------------------------------------------------------

        GLOBAL  delay10_R

; example of using Shared Uninitialized Data Section
        UDATA_SHR
dc1     RES     1
dc2     RES     1
dc3     RES     1

        CODE
delay10_R
        banksel dc3
        movwf   dc3
dly2    movlw   .13
        movwf   dc2
        clrf    dc1
dly1    decfsz  dc1,f
        goto    dly1
        decfsz  dc2,f
        goto    dly1
        decfsz  dc3,f
        goto    dly2

        retlw   0

        END
blink.asm
;******************************************************************************
;                                                                             *
;    Filename:      blink.asm                                                 *
;    Date:          2012/11/14                                                *
;                                                                             *
;    Author:        Mathew Mrosko                                             *
;                                                                             *
;******************************************************************************
;                                                                             *
;    Files required: P12F675.INC                                              *
;                                                                             *
;******************************************************************************
;                                                                             *
;    Notes:         Debugging routine.  Write a number to W and call the      *
;                   blink subroutine and we will blink a test pin a number    *
;                   of times equal to the value in W.                         *
;                                                                             *
;******************************************************************************

;------------------------------------------------------------------------------
; PROCESSOR DECLARATION
;------------------------------------------------------------------------------

#INCLUDE           ; processor specific variable definitions

;------------------------------------------------------------------------------
; VARIABLE DEFINITIONS
;------------------------------------------------------------------------------

        GLOBAL  blink_R
        EXTERN  delay10
        EXTERN  sGPIO

; example of using Shared Uninitialized Data Section
        UDATA_SHR
loop_ct RES     1

        CODE
blink_R
        banksel loop_ct
        movwf   loop_ct         ;store the number of blinks
        bcf     STATUS,C        ;set C=0 for multiply
        rlf     loop_ct,F       ;double it for the on/off
        movlw   .1              ; w=1
        addwf   loop_ct,F       ;add w to loop_ct for initial toggle
blink_loop
        movf    sGPIO,w         ;restore our shadow register to w
        xorlw   b'00100000'     ;toggle w (with an xor)
        movwf   sGPIO           ;save back to shadow register
        movwf   GPIO            ;save to actual GPIO
        movlw   .50             ;load decimal 50 for a 50ms delay
        ;pagesel delay10         ;call our delay function
        call    delay10

        ;pagesel loop_ct
        decfsz  loop_ct,F       ;decrement our loop counter and
        goto    blink_loop      ;       skip our loop if 0

        retlw   0

        END

Binary

Strobe Firmware (558 downloads)

DC SSR

I briefly tested the DIGWDF675 being driven directly from the output of the PIC16F688, but the current requirements of the 675 controller exceed the 16F688 pin capability, so I put a DCSSR in between the Renard 64 output and the DIGWDF675. This added to cost and complexity, but should (**knocks on wood**) work reliably for me throughout the show.

Problems

ADC

I tried using an ADC read to seed the random generator, but that wasn’t working properly. I likely had something configured incorrectly on the chip since the code seemed to function, but always gave me a 0 when reading. This is indicative of having ^MCLRE set, which originally I did, but I didn’t realize that would result in GP3 ADC reads of 0 before switching solutions and haven’t gone back to test. My solution for this problem was suggested by a member of DIYC. Don (maybe one of the most creative, think-outside-the-box people at DIYC) suggested the EEPROM read/write to seed the random generator. Don’s website can be seen here: http://www.eshepherdsoflight.com/

Testing

I developed the code for the PIC12F675 using a breadboard and a ChipClip. I finally had everything working (or so I thought) and put the µC in the board which was assembled a few weeks ago. Once I did this, I encountered 3 problems.

  • Cat5 needed to be rotated 180° – Another stupid mistake I made when trying to save money by not using RJ45 connectors and directly soldering Cat5 between the two PCBs was having the wires 180° out of phase. This was a simple re-solder job fix.
  • MCLR – My original firmware.asm file had _MCLRE_ON which meant if pin4 of the PIC12F675 wasn’t pulled high the chip would reset. The ^MCLR pin is high with my PICkit2/ChipClip solution, so my breadboard worked, the pin is floating on Dirk’s DIGWDF design, so I had undefined behavior. The chip would be in reset mode until my hand went close to the pin which apparently was enough to get it above some threshold. Originally I suspected cabling or a cold solder joint, but realized I didn’t even have to move the circuit, just get near it for it to start working. Once I made the change to the chip configuration to disable ^MCLR, the code worked as expected.
  • Continuous Power – The Renard 64 was in storage, so I used a Simple Renard 32 I had more available as a method of driving the DC SSR circuit. I was using my hephaestus tester to send Renard protocol data of 100% intensity, yet the circuit seemed to be in reset mode. I could see the faintest flicker on the strobes. Turns out the firmware of the Simple Renard 32 has roughly a 5ms break in it’s power output which was enough to put the PIC12F675 into freak-out mode.

Enclosure

To save money, one of the other things I took into consideration was the enclosure. In addition to being cheap, the two boards I chose (DIGWDF675 and DirkCheapSSR) both fit easily into 1″ PVC. This became my enclosure which I sealed up using hot glue in case I need to service/troubleshoot the configuration.