Is picaxe-to-picaxe coordination practical?

wapo54001

Senior Member
I'm looking, and can't find, practical information on how to datalink two picaxes together. Maybe there's no info because it's really not practical.

Recently, there was a discussion about controlling the opening and closing of drapes, and the need to cut power to the motor instantly when the drapes reach their limit. A discussion ensued as to how best to implement an instant cutoff of power to the motor while the processor was doing other duties. I thought that this would be a perfect task to assign to a separate 08M controller -- watch the motor current as its only task while another controller handled the rest of the program, but no one else picked up on that idea.

Likewise, the thread regarding using an 08M as a variable voltage regulator is another example of giving one chip a singular job -- of regulating a variable voltage -- while another larger chip provides other intelligent services for a really nifty bench instrument. But how do you get one chip to pass a numeric value to the other?

I have in mind other projects that I'd like to try that would be best implemented by networked picaxes. In all of these cases, there is a need for one picaxe to supervise another picaxe, and that means setting up communications between chips. But I can't seem to find any how-to tutorial or information on how to get one picaxe to pass data to another picaxe in a way that a beginner like me can understand and implement.

If such exists, could someone point it out? Or is the subject just too varied and/or difficult to describe in a practical, as opposed to theoretical, manner?
 

Technical

Technical Support
Staff member
One common method is to use pulsout/pulsin to send/receive a variable value.
You can also use serout/serin but that has restrictions on the parts without a timeout.

If you have a X1 setup you can use i2c, with one PICAXE as a master and one as a slave. The Master does a write to update the values in the slaves scratchpad memory, the slave just waits for that memory address to be updated (can do other things at the same time).
 

wapo54001

Senior Member
In the ideas I've thought up, I think of the lowly 08M as doing the simple work with a larger chip doing the coordinating, so the X1 i2c isn't viable.

I've looked at serin/serout and scratched my head some because the information in the manual are for two separate commands, and there is no description of how they interrelate; was hoping someone had a cookbook approach published somewhere.

Is there a schematic of how two chips might be wired together -- data connection and handshaking if required?

Is there a handshaking line required to tell the other chip that data is ready to be sent?

How does the sending chip know when the receiving chip has gotten the data?

Maybe I'm making this more complicated than it needs to be, but I don't think so.
 

BeanieBots

Moderator
This has actually been covered countless times here.
Connect an output of the sending chip to an input on the receiving chip.
The sender (Tx) sends using serout, the receiver (Rx) recieves using serin.
The "problem" is that once the Rx enters serin, it will wait until a character is received.
The way around this (unless you use a 28X1) is to use another input/output connection to "tell" the Rx that a character is about to be sent or use it to trigger an interrupt.
Have a browse around the archives and finished projects sections. It really has been thrashed and thrashed.
 

wapo54001

Senior Member
This has actually been covered countless times here.
Connect an output of the sending chip to an input on the receiving chip.
The sender (Tx) sends using serout, the receiver (Rx) recieves using serin.
The "problem" is that once the Rx enters serin, it will wait until a character is received.
The way around this (unless you use a 28X1) is to use another input/output connection to "tell" the Rx that a character is about to be sent or use it to trigger an interrupt.
Have a browse around the archives and finished projects sections. It really has been thrashed and thrashed.
Yes, I've read quite a lot of it. Lots of stuff that describes the challenges, I just haven't seen anything that supplies the answers. I'll look again, and focus on the finished projects.
 

BeanieBots

Moderator
That's because there is no definative answer. It all depends on what you are doing. How much data needs to be sent, does it need to be bi-directional.
For example, one of your examples was monitoring current.
For that particular case, I'd use a comparitor and have it trigger an interrupt.
PWMout could set the trip point. All done with one 08M and instant monitoring.
If it's just a single byte that needs to be always available, then pulsout/pulsin as suggested by Technical. If it's just a nibble, then servo/pulsin. You can also use PWMout/pulsin.
For more characters, then serout/serin but then you need to think about how not to "hang" waiting.
For complex fast bi-directional comms, then I2C with a 28X1.
 

BCJKiwi

Senior Member
How much and what sort of information is to be exchanged?

The curtain control 2 picaxe was not followed up (by me anyway) as one PICAXE was more than adequate for that application.

If only a few status flags need to be sent then this can be done with PWM/ADC. RC the pwm to get a steady voltage, read the voltage level with ADC. The voltage level is your information. With ReadADC10, at least a few hundred discrete messages can easily be achieved, probably more. Handshaking can be done with an output and an input tied together if there is more than one reciever, else anything above 0V that is stable is enough.

sender;
Set up PWM level
Output high

Receiver;
sees input high
Reads ADC
Decodes ADC count to useful information.
 

moxhamj

New Member
I can post some code and links, but it helps to know a few more details. Is speed important? Are the slave nodes acting as sensors, switching things on/off, or both? How much data do you need to send? Is the data an on/off state, a byte, or many bytes of data? How long are the connections between master and slave? How often does the data packet need to go through?
 

moxhamj

New Member
This question has got me thinking further. I agree that it is difficult to send data between two picaxes. Simple highs and lows are easy, and pwmout/readadc is ok if you don't mind losing accuracy. Serout works fine if you are happy to have a picaxe hanging all the time waiting for the data, but that receiving picaxe won't be able to do anything in the meantime.

I've started coding another solution. I'm using pulsout and pwmout and I'm sending 11 bytes of data which have been stored by another routine in the scratchpad ram. Each byte is split into 8 bits, and each bit is sent with a low represented as a 0.75ms pulse, and a high is a 1.5ms pulse. There is a delay after each bit, and a delay after each byte (so the byte can be stored), and I'm experimenting with how short those delays can be.

The beauty of this is that it times out if the data gets corrupted - ie if a pulse is outside the reference range of (say) 0.5 to 2ms, then the whole packet is abandoned.

A packet can be started with a longer high pulse - and the main routine can poll input pins looking for highs. So a picaxe can be polling a number of inputs, and can thus act as a router.

Back to coding...
 

BeanieBots

Moderator
An interesting idea Doc.
Instead of simply trying to see how short the pulses can be made, why not go for something like the SPI protocol. Use two lines, one is data, the other is clock. The clock triggers an interrupt. When the interrupt routine is entered, the data line is either high or low.
Endless possibilities can then be considered. eg the slave sends a "ready for next bit (interrupt) back to the master.
 

moxhamj

New Member
Not a simple task! Lots of timing tweaking.

Call the get_packet routine regularly in a main loop, eg 5x a second at least.

Enables scanning on multiple input pins, and output to multiple pins, so the picaxe can route messages. Coded for an 18X - code space limitations might make this tricky to fit into the smaller chips. If multiple inputs are used, pull down input pins with 10k resistors so unused pins are definitely low.

Code:
Send_Packet:' sends data packout out using pwm data protocol. To pin number in b5 eg on an 18X can be pin 0 to 7
	' 11 bytes, scratchpad 210 to 221	
	' this can't do radio, as random noise will start pwm and then will hang for 0.65535 seconds and miss the data packet
	' so if it is not radio, then no need to worry about making the high and low periods the same
	' we are storing 11 bytes - pauses between each bit so can store
	' send LSB first like RS232 (but no need for start or stop bits)
	' a low is a 0.75ms pulse, a high is a 1.5ms pulse
	low b5' so definitely low
	pulsout b5,25000' wakeup pulse 250ms
	pause 300' wait 300ms with line low (must be shorter than 0.65535 secs). 300ms is a good compromise.
	for b0=210 to 220' scratchpad locations - 11 bytes
	  peek b0,b1' get the byte
	  for b2=1 to 8
	    b3=b1 and %00000001' test the right bit
	    if b3=0 then
	        pulsout b5,75' send a 'low' pulse
	      else
	        pulsout b5,150' send a 'high' pulse
	    endif
	    pause 20' wait a bit - time for getpacket to store bytes, do calcs etc. 20 is reliable. 10 is mostly reliable
	    b1=b1/2' shift right
	  next b2
	next b0
	return
	
Get_Packet:' test for packets arriving on pins 6,2, and 1 (example for an 18X, can change for other picaxe chips)	
	let b7=pins' read pins
	b7=b7 and %01000110' mask off other input pins		
	select case b7' can't convert directly to a number because small chance two lines might be high at the same time
	  case %00000010
	    b1=1' change pins binary to pin number
	  case %00000100
	    b1=2
	  case %01000000
	    b1=6
	  else
	   b1=0' zero if all tested input pins are zero
	endselect
	if b1<>0 then' now know which pin to read
	  pause 300' pin goes high for 250ms and low for 300ms so if it was high, then if wait 300ms then it must be low. 
	  b6=210' scratchpad location to store
	  for b4=1 to 11' count 11 bytes 
	    b7=1' value to add 
	    b0=0' start as zero, read LSB in first
	    for b5=1 to 8' count 8 bits, build byte b0
	      pulsin b1,1,w1' read pulse
	      if w1>100 then
			b0=b0+b7 ' assume if <100 then is a zero - a checksum on this packet sorts out any errors
	      endif
	      if w1=0 then goto abortpacket' should be 73-77 and 148 to 152. Zero means timeout.
	      b7=b7*2' value to add multiply by 2
	    next b5
	    poke b6,b0' store the byte
	    b6=b6+1' increment the scratchpad location
	  next b4
	endif
abortpacket:return
 
Last edited:

krypton_john

Senior Member
Dr. Acula,

Wow. This is going to take a while to absorb and try out. But it sure is exciting!
On the subject of using X parts and i2c vs 08M parts and various complicated methods: I like the 08Ms as it is a challenge to come up with clever ideas for these little gems. However if you want to just get something done, and you want it to be simple, transparent, maintainable, expandable and efficient, you just cannot beat the X parts IMHO.

I don't buy the cost saving argument as I put a value on my time. If I save one hour of design time that is worth enough to cover the extra cost of dozens of X parts.

What about volume production you ask? I would say don't use PICAXE for volume commercial products. Prove the concept with a PICAXE then implement the production design with a PIC micro.

Just my NZ$0.02 worth.

Cheers,
JohnO
 

moxhamj

New Member
I'm going to try some more code tonight as I think this can be improved. Pulsin/pulsout is very fast - short pulses are faster than a picaxe instruction. Most of the time is spent between pulses waiting till the receiver processes the data.

The error in pulsin is very small - sometimes a 75 comes in as a 74 but mostly as a 75. That probably would get worse with longer cables as the capacitance of the cable would slow the rise times, but for local comms the cables will be under a metre anyway.

So - I'm going to try sending a byte all at once. Multiply pulsin by 8 or 16 and add a constant so a zero is never sent as a zero (zero is the timeout value). Pulsout sends a word so a binary value of 0 could become 16+16*0=16. 1 would become 16+1*16=32 and 255 would become 16+255*16=4096. If I use 16 rather than 10, the integer rounding is not an issue, because dividing by 16 just shifts the bits to the right by 4 places.

The delay for a byte becomes less than 1ms to a max of 41ms. There might be a 20ms delay like with the bit code to do the saves etc. So a byte should take 61ms at most. I think this compares favourably with the bit code which is taking 8*20+20=180ms. And I think the code will be shorter. That is the theory - will do some experiments this evening.
 

moxhamj

New Member
All tested. Faster, simpler, & less code space.

Another advantage over serin is that this system can read more than 14 bytes in one packet. In fact, the packet size is only limited by the scratchpad ram size.

Pulsin errors are at most +/-1 (with a 50cm cable), so multiplying by 16 could be overkill. Multiplying by 8 would still work, but the speed is mainly limited by the time the receiver needs to store the byte, so sending the byte faster doesn't gain much.

If the picaxe only ever sends/receives from one particular pin, then the receive code can be even shorter as there is no need to test other pins.

I'm sure the pause 20 could be shorter - but it is plenty fast enough for me.

PS - a big thanks to Technical for suggesting this approach in post #2. I have been stuck on serin for a long time and this is a much better solution in many ways. This code is going straight into a working project.

Code:
Send_Packet:' sends data packout out using pwm data protocol. To pin number in b5 eg on an 18X can be pin 0 to 7
	' 11 bytes, scratchpad 210 to 220
	' this can't do radio, as random noise will start pwm and then will hang for 0.65535 seconds and miss the data packet
	' so if it is not radio, then no need to worry about making the high and low periods the same
	' we are storing 11 bytes - pauses between each one so can store
	low b5' so definitely low
	pulsout b5,25000' wakeup pulse 250ms
	pause 300' wait 300ms with line low (must be shorter than 0.65535 secs). 300ms is a good compromise.
	for b0=210 to 220' scratchpad locations - 11 bytes
	  peek b0,b1' get the byte
	  w6=b1+1' so never sends a zero
	  w6=w6*16' order is left to right
	  pulsout b5,w6' send value
        pause 20' wait a bit - time for getpacket to store bytes, do calcs etc. 20 is reliable. 10 is mostly reliable
	next b0
	return
	
Get_Packet:' test for packets arriving on pins 6,2, and 1 (example for an 18X, can change for other picaxe chips)	
	let b7=pins' read pins
	b7=b7 and %01000110' mask off other input pins so only look at pins that expecting an input from	
	select case b7' can't convert directly to a number because small chance two lines might be high at the same time
	  case %00000010
	    b1=1' change pins binary to pin number
	  case %00000100
	    b1=2
	  case %01000000
	    b1=6
	  else
	   b1=0' zero if all tested input pins are zero
	endselect
	if b1<>0 then' now know which pin to read
	  pause 300' pin goes high for 250ms and low for 300ms so if it was high, then if wait 300ms then it must be low. 
	  for b4=210 to 220' count 11 bytes to scratchpad location
	    pulsin b1,1,w6' times out after 11*0.65 seconds if an error occurs
	    w6=w6/16-1' order left to right
	    poke b4,b12' store the byte
	  next b4
	endif
	return
 
Last edited:

moxhamj

New Member
I'm still debugging this. Works fine between two chips from the same batch, but finding some problems with an 08M talking to an 18X. I'm back to retesting the code in post #11 as this is more tolerant of timing errors.

Addit - code in post #11 is the one to use. Much more reliable, and only a bit slower.
 
Last edited:

wapo54001

Senior Member
Dr. Acula,

Regardless of revious posts claiming this problem has been discussed to death and solved repeatedly, your solution is the first one that I know of that is actually published and has the potential to transfer substantial data with convenience and total accuracy with very low overhead. I hope that all of the issues can be smoothed out.
 

moxhamj

New Member
I agree - I don't think it really has been solved. The problem is how to send multiple accurate bytes from one chip to another, and still have the chip able to do other things as well. I am sure this code can be optimised - maybe by shortening the pulse widths or by shortening the delays, or both. This is at least working and it can send data between similar chips (18Xs from different batches) and between different picaxe chips (08s to 18xs and vice versa).

This is a protocol that I'm using for both comms on a single board between picaxe chips, and between picaxe chips that are near each other (<1 metre).

Just for completeness though, I'm adding up the bytes and using the last two bytes as a checksum.

Just as an example:

(note how the get_packet routine can jump to the end if there is no wakeup pulse, and monitor several input pins. You can't monitor several pins at the same time using serin. This enables testing many times per second, and the chip can be doing other tasks in the meantime)

Code:
Send_Packet:' sends data packout out using pwm data protocol. To pin number in b5 eg on an 18X can be pin 0 to 7
	' 11 bytes, scratchpad 210 to 220
	' this can't do radio, as random noise will start pwm and then will hang for 0.65535 seconds and miss the data packet
	' so if it is not radio, then no need to worry about making the high and low periods the same
	' we are storing 11 bytes - pauses between each one so can store
	low b5' so definitely low
	pulsout b5,25000' wakeup pulse 250ms
	pause 300' wait 300ms with line low (must be shorter than 0.65535 secs). 300ms is a good compromise.
	for b0=210 to 220' scratchpad locations - 11 bytes
	  peek b0,b1' get the byte
	  for b2=1 to 8
	    b3=b1 and %00000001' test the right bit
	    if b3=0 then
	        pulsout b5,75' send a 'low' pulse
	      else
	        pulsout b5,150' send a 'high' pulse
	    endif
	    pause 20' wait a bit - time for getpacket to store bytes, do calcs etc. 20 is reliable. 10 is mostly reliable
	    b1=b1/2' shift right
	  next b2
	next b0
	return
	
Get_Packet:' test for packets arriving on pins 6,2, and 1 (example for an 18X, can change for other picaxe chips)	and pin 7 from radio 08
	let b7=pins' read pins
	b7=b7 and %01000110' mask off other input pins so only look at pins that expecting an input from	
	select case b7' can't convert directly to a number because small chance two lines might be high at the same time
	  case %00000010
	    b1=1' change pins binary to pin number
	  case %00000100
	    b1=2
	  case %01000000
	    b1=6
	  case %10000000
	    b1=7 ' from radio 08 buffer picaxe  
	  else
	   b1=0' zero if all tested input pins are zero
	endselect
	if b1=0 then goto end_packet' no packet so exit subroutine
	' now know which pin to read
	  pause 300' pin goes high for 250ms and low for 300ms so if it was high, then if wait 300ms then it must be low. 
	  b6=210' scratchpad location to store
	  for b4=1 to 11' count 11 bytes 
	    b7=1' value to add 
	    b0=0' start as zero, read LSB in first
	    for b5=1 to 8' count 8 bits, build byte b0
	      pulsin b1,1,w1' read pulse
	      if w1>100 then
			b0=b0+b7 ' assume if <100 then is a zero - a checksum on this packet sorts out any errors
	      endif
	      if w1=0 then goto end_packet' should be 73-77 and 148 to 152. Zero means timeout.
	      b7=b7*2' value to add multiply by 2
	    next b5
	    poke b6,b0' store the byte
	    b6=b6+1' increment the scratchpad location
	  next b4
	'now have the numbers in 210 to 220
	peek 210,b12
	peek 211,b13
	peek 212,b2
	peek 213,b3
	peek 214,b4
	peek 215,b5
	peek 216,b6
	peek 217,b7
	peek 218,b8
	peek 219,b10
	peek 220,b11
    	' now check for valid data packet etc
     	w0=b5+b6+b7+b8+w6' work out the checksum = data bytes plus the random header ID
     	if w0=w5 then' checksum is correct
     		pulsout 0,10000' flash the yellow led just to indicate this is a packet with a valid checksum
     		peek Register_Last_Random_ID,word w5' get old message number. Overwrites the checksum as don't need it any more
    		poke Register_Last_Random_ID,word w6' store this message id number.
     		if w5<>w6 then' different message number so act on it
           		'store this message in case PC asks for it
            		if b4=5 then 
            		  gosub Act_On_Message_5' message from PC to this node requesting last message dump. No need to do rest of subroutine.
             		  goto end_packet ' skip to end
             		endif
             		gosub StoreMessage' in case PC asks for it
            		' test whether message is for me. If yes, act on it. If no then forward it on to everyone else
            		if b3=My_ID_NUmber then' message is for me
            		      poke Register_FromNode,b2
            		      poke Register_ToNode,b3
	               		poke Register_New_Text,1' flag for display
	               		' work out what to do with this message
               			select case b4
            			'case 5  see above - message 5 doesn't have to match ID numbers, just has to come from a port on this module
					case 8
					   gosub Act_On_Message_8' message from sensor node
					case 9
					   gosub Act_On_Message_9' program node
					case 10
					   gosub Act_On_Message_10' send analog data to PC at PCs request
					case 11
					   gosub Act_On_Message_11' turn node back into a router
               			endselect
              	         else' not for me so forward on
                 			gosub Send_Message' forward on 
            		endif
          	endif
     		poke Register_New_Text,1' update text  
      endif
end_packet:return
and

as an example of Serin to pulsout protocol - this code is sitting on a dedicated 08M

Code:
' reads pin 2 from PC, outputs to pin1 in pulsout format

main:serin 2,T2400,("ID"),b12,b13,b2,b3,b4,b5,b6,b7,b8,b10,b11
	poke 80,b12
	poke 81,b13
	poke 82,b2
	poke 83,b3
	poke 84,b4
	poke 85,b5
	poke 86,b6
	poke 87,b7
	poke 88,b8
	poke 89,b10
	poke 90,b11
	b5=1' send to pin 1
	gosub send_packet
	goto main


Send_Packet:' sends data packout out using pwm data protocol. To pin number in b5 
	low b5' so definitely low
	pulsout b5,25000' wakeup pulse 250ms
	pause 300' wait 300ms with line low (must be shorter than 0.65535 secs). 300ms is a good compromise.
	for b0=80 to 90' scratchpad locations - 11 bytes
	  peek b0,b1' get the byte
	  for b2=1 to 8
	    b3=b1 and %00000001' test the right bit
	    if b3=0 then
	        pulsout b5,75' send a 'low' pulse
	      else
	        pulsout b5,150' send a 'high' pulse
	    endif
	    pause 20' wait a bit - time for getpacket to store bytes, do calcs etc. 20 is reliable. 10 is mostly reliable
	    b1=b1/2' shift right
	  next b2
	next b0
	return
 
Last edited:

BeckettM

New Member
I'm looking, and can't find, practical information on how to datalink two picaxes together. Maybe there's no info because it's really not practical.
I have a trip system installed at a ski field that uses a 28x to detect the inputs, and display the status/cause, which feeds data to a 08M. Serout at 2400 is used to pass three numbers to the 08M, which then pulses an output which controls the diesel solenoid.

This arrangement was choosen to 1: Ensure both chips operated in fail safe 2: To allow the 08M to pass on the information through an external connection to a remote monitor.

The whole things works very well, and is fast enough to be nearly instant the moment the system trips.

Cheers
Mark :)
 

moxhamj

New Member
I wonder about another theoretical system. Yet to be breadboarded but it has potential. Replicate the I2C system of sorts.

Two wires. Clock and data. Clock stays low normally. If it goes high then some data is on the way. Read data on the rising clock edge. Timeout if no clock for n milliseconds.

Lines pulled low with 10k resistors. Wire OR onto lines with 914 diodes. Use IO pins on an 08M - eg pins 1 and 2, so can output but can also read. So, pin 1 might be the clock, and it goes to the common clock bus with a diode, and also reads the bus via a 100k resistor.

This is not dependent on clock frequencies. It can talk to other micros without the use for uarts and uart crystals. It times out if the clock doesn't do anything for a while. It never hangs. Chips just need to keep polling the clock line. Data clashes are less likely because a chip will poll for a quiet clock line for a bit till it sends some data.

Not sure how fast it can run - possibly faster than the pulsout system, because the wakeup pulse is not needed (especially if it wakes up with an interrupt).
 
Top