| ||||||||||||||
| ||||||||||||||
|
||||||||||||||
Emerging behavior: the wave (4 x 12F675)By Bert van Dam
If you look at people in a soccer stadium doing the wave it looks like a giant orchestrated phenomena. But of course it isn't. Every spectator follows a very simple set of rules:
This phenomena is called emerging behavior. A set of small simple rules for (small brained) individuals results in seemingly intelligent group behavior. Not everybody follows exactly the same rules, otherwise in the example above the wave would simply never start. Unless someone fancied a hotdog and got up to get one, accidentally setting off a wave. So maybe you need a fourth rule, something like
The same thing applies to ants, termites, and even shop distribution across a city. If you're interested in this 'Emergence, the connected lives of ants, brains, cities and software' by Steven Johnson might be a good choice for you (No. No link to Amazon, I don't get paid for this). I suppose the software equivalent of a wave would be a running light. It would be rather easy to simply program a running light into a single microcontroller, but of course that wouldn't be any fun. So instead we'll use four separate controllers, each with their own set of rules, who look at each other and respond to each other. Like this:
The LED on a module is 'spotted' by the LDR on the next module. After a short delay this module flashes its LED, which alters the next module, and so on. Important is that each module runs completely independent (and completely 'unaware') of the other modules, yet when put together they form a running light.
Since I don't have an unlimited supply of parts I've limited myself to four modules, but it would be rather interesting to see what would happen with, say, 100 of these modules in one room. Anyway, obviously all four units look exactly the same:
The units need to be placed in such a way that each unit's LDR can see the LED of the previous unit, like this:
Note that I have added several 0.1 uF capacitors to eliminate interference over the power lines. The long wires attached to the top right hand unit are from the Xwisp in-circuit programmer. Special feature is the auto calibration. Since the modules are supposed to react to the LED only, and not to changing ambient light conditions recalibration needs to take place on a regular basis, for example with a variable resistor. This is of course no fun with four units, so the units calibrate themselves.
-- take a sample
PIC_ADC_read_low_res (2, RESIST)
-- get a random value
RANDOMVAL = random_byte
-- check if a light has been observed by the LDR
-- and if so turn on the own light after a short delay
if RESIST < TRESHOLD then
-- see if you feel like participating
if RANDOMVAL > 50 then
SENDDATA
delay_10ms( REACTIONTIME )
LEDON
end if
end if
-- calculate new threshold value
THRESHOLD = RESIST / 10
THRESHOLD = THRESHOLD * 9
The code segment above takes a measurement of the LDR. If this is below the threshold value (meaning the LED of the neighbor unit is on), the signal is processed. If that is not the case a new threshold value is calculated. Since the neighbor LED is off this new threshold value is based on the current ambient light conditions. The resistor network (with the two 4k7 resistors) has been selected to function optimally under low ambient light (because I only program at night). -- turn the LED on and off procedure LEDON is LED = high LIGHT = 1 SENDDATA delay_10ms( ONTIME ) LED = low LIGHT = 0 end procedure Once the signal is recognized as being from a neighbor unit the unit's own LED is flashed. Two variables control this process:
You can play with these to variables to make the wave go faster or slower, flash short or long. If the units would only react to their neighbors but never take any action themselves the wave would never start. So every once in a while (in fact in 2% of the loops) each unit flashes spontaneously:
-- every once in a while decide to light the LED just because...
if RANDOMVAL > 250 then
LEDON
end if
By the same token if every unit kept doing the wave it would never stop, so in 20% of the loops a unit will simply not participate.
if RANDOMVAL > 50 then
SENDDATA
delay_10ms( REACTIONTIME )
LEDON
end if
It is fascinating to watch the LED's. Sometimes they perform perfect waves, at other times it's completely quiet, or two of them start a wave at the same time. These wave chase each other until they catch up, or a unit stops. Serial communications aren't really needed, but I did use if for debugging, so I thought I'd leave it in, and write a small Visual Basic application to display what's going on inside a unit:
This is rather straight forward, except that the routine to switch the Xwisp programmer to pass through mode is now incorporated, which is of course much easier:
'enable communications
MSComm1.PortOpen = True
'switch Xwisp programmer to pass through mode
'Set the Break condition for 0.1 second to switch the
'programmer to attention mode
MSComm1.Break = True
Duration! = Timer + 0.1
Do Until Timer > Duration!
Dummy = DoEvents()
Loop
MSComm1.Break = False
'switch to programmer speed
MSComm1.Settings = "19200,n,8,1"
'send pass through command
MSComm1.Output = "0000p"
'wait to make sure the command is processed by
'the programmer
Duration! = Timer + 0.1
Do Until Timer > Duration!
Dummy = DoEvents()
Loop
'switch to microcontroller speed
MSComm1.Settings = "1200,n,8,1"
You can download the source here, but of course you'll need Visual Basic in order to actually run it. This is the full JAL source. It needs to be downloaded to each and every unit:
-- jal 4.55
include JRTL
include f675_4i
include jascii
-- random library
include random3
-- AD converter settings
const PIC_ADC_Nchan = 3
const PIC_ADC_NVref = 0
const PIC_ADC_Rsource = 10_000
const PIC_ADC_high_resolution = false
-- serial communications settings
const asynch_baudrate = 1200
const asynch_polarity = active_high
const asynch_parity = parity_none
const asynch_stopbits = 2
-- serial variables
var volatile bit asynch_in_pin is pin_a1
var volatile bit asynch_in_direction is pin_a1_direction
var volatile bit asynch_out_pin is pin_a0
var volatile bit asynch_out_direction is pin_a0_direction
-- serial library
include seriali
-- AD converter variables
var volatile byte ADCON0 at 0x1F
var volatile bit GO at ADCON0 : 1
var volatile byte ADRESH at 0x1E
var volatile byte ADRESL at 0x9E
var volatile byte ADCON1 at 0x9F
-- AD library
include pic_adc
-- define the pins
pin_a2_direction = input
pin_a4_direction = output
-- general variable
var byte RESIST, REQUEST, LIGHT, THRESHOLD, REACTIONTIME, ONTIME,RANDOMVAL
-- define the variables used
var bit LED is pin_a4
-- initialize the AD converter
PIC_ADC_init
-- some initial values for the variables
THRESHOLD = 0 -- start with 0 threshold
LED = low -- start with the LED off
LIGHT = 0 -- indicator that LED is off
REACTIONTIME = 20 -- reaction time in 10ms
ONTIME = 10 -- time that the lED is on in 10ms
-- send data to PC procedure
procedure SENDDATA is
asynch_send( RESIST )
asynch_send( LIGHT )
asynch_send( THRESHOLD )
asynch_send( REACTIONTIME )
asynch_send( ONTIME )
asynch_send( RANDOMVAL )
end procedure
-- turn the LED on and off
procedure LEDON is
LED = high
LIGHT = 1
SENDDATA
delay_10ms( ONTIME )
LED = low
LIGHT = 0
end procedure
forever loop
-- take a sample
PIC_ADC_read_low_res (2, RESIST)
-- get a random value
RANDOMVAL = random_byte
-- check if a light has been observed by the LDR
-- and if so turn on the own light after a short delay
if RESIST < THRESHOLD then
-- see if you feel like participating
if RANDOMVAL > 50 then
SENDDATA
delay_10ms( REACTIONTIME )
LEDON
end if
end if
-- calculate new threshold value
THRESHOLD = RESIST / 10
THRESHOLD = THRESHOLD * 9
-- call procedure to send data to the PC
SENDDATA
-- give the computer time to react
delay_100ms
-- every once in a while decide to light the LED just because...
if RANDOMVAL > 250 then
LEDON
end if
end loop
Submitted: 06/02/2005 Article content copyright © Bert van Dam, 2005.
|
|
|||||||||||||
All content copyright © 1998-2007, Generation5 unless otherwise noted.
- Privacy Policy - Legal - Terms of Use -