Audio Command Remote PICAXE via Morse Code (Proof Of Concept)

mrburnette

Senior Member
When one asks a question, even rhetorical, sometimes an answer is required to put the thought to rest. Such is the case with a recent blog post, Command-and-Control-via-Morse-Code and so I set about seeing if I could prototype something quickly and simple enough that others could play with the concept without pulling out their hair. On the first part, I have something simple but I will not be responsible for hair lost since the POC is pretty brute-force in implementation.

On a PICAXE 08M2+, I implemented the Magic Morse algorithm, stripped bare. I then implemented a master Do-Loop and a quasi-control structure build pitifully from IF-Then-ELSE statements... mainly because it is easy to follow the logic here.

The Morse Code is generated by a 650Hz sine wave from my function generator at about 2Vp-p and is clipped by a 2N3904 with a 1K ohm resistor in the base circuit from the Morse Key and a 10K to +5 in the collector-PIC C.3 connection. This gives a very decent square wave to C.3 when viewing with a scope and the signal is active HIGH due to the pullup.

I implemented 3 control channels, but fell back for the POC to just using 2 due to the desire to have a serial link to my PC via serout which I dedicated to C.1. In use, this link would not be necessary and C.1, C.2, and C.4 could be used for control output.

I implemented only 2 verbs: High and Low. This could be expanded to include Input and Output, etc.

I implemented a crude 3-byte input queue such that all commands to the PICAXE must begin with the Morse decoded letter "C" and if not, the queue would flush. Once the first element is "C" the second element must be either "1", "2", or "4" to represent the port number... in the POC port C.1 is used for serial and is only implemented as a skeleton.

The third parameter is the method invocation, a "0" for LOW or a "1" for HIGH. Any other value is not processed and the queue is automatically cleared and returned to the Do-Loop to be provisioned with Morse received characters.

Here is the code: note that a significant amount of size can be reduced by eliminating the serout statements.

Code:
' Morse Test Command & Control.BAS
' 572 Bytes / 2048
' NOTE: 9600 BAUD is sent to RS232 PortC.1 Physical Pin #6
' Test program by Ray Burnette
' Magic Morse is (c) 2011 by M. Ray Burnette, all commercial rights retained

#picaxe 08m2
#Terminal 9600
#NO_DATA	' REMARK THIS LINE DURING INITIAL PICAXE PROGRAMMING!!!

' SYMBOL table - aliases for byte and word variables
{
SYMBOL DitDah      = B0
SYMBOL CharCount	 = B1
SYMBOL DIT         = B2
SYMBOL Point       = B3
SYMBOL Flag        = B4
SYMBOL Pflag       = B5
SYMBOL InputPulse  = W3
SYMBOL Temp        = B8
SYMBOL Duration    = W5
; Command variables
SYMBOL W		 = B24
SYMBOL X		 = B25
SYMBOL Y		 = B26
SYMBOL Z		 = B27
; Command structure = 3 bytes: x= Primary_Key, y= Secondary_Key, z= action/state
; x     y      z
; C     1     0|1
; C     2     0|1
; C     4     0|1

' Valid 08m2 SetFreq: k31, k250, k500, m1, m2, m4, m8,m16,m32
SYMBOL CLOCK = m32

SYMBOL BAUD = N9600_32	' For use with Windows XP HyperTerminal
SYMBOL MONITOR = C.1	' RS232 output on this pin

SYMBOL PulseInput   = C.3	' The 'conditioned' Morse Code signal on this pin
' The Input pulses on pin C.3 can be latched on high to low OR low to high
SYMBOL Hi2Lo = 0
SYMBOL Lo2Hi = 1
}

' Initialize program state
{
SetFreq CLOCK

' Set DITDAH discrimination factor
LET DIT = 100

' Wait to ensure all is stable
Pause 4000
serout MONITOR,BAUD,("08M2+ 32MHz Morse Control Test",LF,CR)

' DISCONNECT	' Do Not Inspect C.5 for New Download

GoSub Flush
;HIGH 1 : 
HIGH 2 : HIGH 4	; default state is HIGH
}

' Main Code Block____________________________________
{

Do	; Main Program
	MAIN:
	Gosub Morse
	' Variable Temp contains the ASCII character value received
	INC w				' instruction length counter
	; serout MONITOR,BAUD,(Temp)	' Send to Monitor
	
	; Not intended to be elegant, kind of brute-force for demo
	If w = 1 then
		x = Temp : serout MONITOR,BAUD,(x," ")
		If x = "C" then MAIN
		GoTo FAIL
	Elseif w = 2 then
		y = Temp : 	serout MONITOR,BAUD,(y," ")
		If y = "2" or y = "4" then MAIN
		GOTO FAIL
	Elseif w = 3 then
		z = Temp : serout MONITOR,BAUD,(z," ")
		; Fall throught to subroutine selection
	endif
	
	; Subroutines must perform their own validation on "z"
	if x = "C" and y = "1" then		; Port C.1 z= 0|1 = low | HIGH
		serout MONITOR,BAUD,("Gosub C1:")
		gosub C1
	Elseif x = "C" and y = "2" then	; Port C.2 z= 0|1 = low | HIGH
		serout MONITOR,BAUD,("Gosub C2:")
		gosub C2
	Elseif x = "C" and y = "4" then	; Port C.4 z= 0|1 = low | HIGH
		serout MONITOR,BAUD,("Gosub C4:")
		gosub C4
	endif
	
	FAIL:
	Gosub Flush					; Prepare for next instruction sequence
	serout MONITOR,BAUD,("flush", CR, LF)
	
Loop		' Gather more characters
}

Morse:' Differentiates between DITs and DAHs on the PulseInput PIN
{
  ' Initialize TOP-Level variables, flags, and pointers
  Flag = 0 : Pflag = 0 : Point = 0 : DitDah = 0
  
  Do	' loop converts individual Morse elements into a Character
	Duration = 0
	Do	' INPUT-loop counts DITs or DAHs carrier (BFO) frequency
	' 64MHz = 0.625us unit 41mS timeout: Manual2 page 160
		PULSIN PulseInput, Hi2Lo, InputPulse	' =0 if timeout
		If InputPulse = 0 then	Exit
		INC Duration : Flag = 0
	Loop	' Loop while signal is on port PulseInput (active low)

	' InputPulse = 0 Therefore we know that the input has returned HIGH 
	' Set a Flag: dah/dit OR one of the space conditions: element,character,word
	' Only want to detect DIT / DAH when Flag = 1, Conclude when Flag = 5
	INC Flag	' Count number of consecutive (timeouts)

	If Flag = 1 AND Duration != 0 THEN
		INC Point : INC Pflag
		IF Duration > DIT then		' DAHS are in the 180's @ 13WPM
			' DAHs are weighted by received position but DITs are only counted
			Select Case Point
				Case 1 : DitDah = DitDah OR %00001000
				Case 2 : DitDah = DitDah OR %00010000
				Case 3 : DitDah = DitDah OR %00100000
				Case 4 : DitDah = DitDah OR %01000000
				Case 5 : DitDah = DitDah OR %10000000
			End Select 
		EndIf
	EndIf

	If Flag = 5 AND PFlag > 0 then' Morse Code in buffer
		EXIT	' Shortcircuit - we are done with this character
	EndIf

   Loop	' Gather more elements
   
   ' The decoded ASCII character is found by creating a pointer into EEPROM
   DitDah = DitDah + Point	' Complete the pointer algorithm
   READ DitDah, Temp		' Read mapped Morse character from EEPROM
   ' Return to main loop with Temp carrying the decoded Morse Code character
   RETURN
}

Flush:' Clear control variables
{
W = 0 : X = 0 : Y = 0 : Z = 0
Return
}

; Actions / Methods
; Routines here are responsible for validating (ignoring) "bad" z-values
{
C1:
	Return	; serial console in use... just abort
	IF z = "0" then
		LOW 1
	else if z = "1" then
		HIGH 1
	EndIf
Return

C2:
	IF z = "0" then
		LOW 2
		serout MONITOR,BAUD,("C2 LOW ") 
	elseif z = "1" then
		HIGH 2
		serout MONITOR,BAUD,("C2 HIGH ")
	else
		serout MONITOR,BAUD,(z," invalid parameter")
	EndIf
Return

C4:
	IF z = "0" then
		LOW 4
		serout MONITOR,BAUD,("C4 LOW ") 
	elseif z = "1" then
		HIGH 4
		serout MONITOR,BAUD,("C4 HIGH ")
	else
		serout MONITOR,BAUD,(z," invalid parameter")
	EndIf
Return
}

' Magic Morse Numbers and Algorithm are (c) 2011 by M. Ray Burnette, Atlanta GA
' DATA for Magic Morse translation
{
' Morse Object: ASCII 5-bit subset mapped into 8-bit eeprom space
' Mappings ", . ? @" which are 6-bit wide but these are
' forced into the 5-bit space by dropping the 6th bit
EEPROM 0, (0,"E","I","S","H","5",0,0,"e") ' e = ERROR = di di di di di di di di
EEPROM 9, ("T","N","D","B","6","-")
EEPROM 18,("A","R","L","w") 	' w = wait
EEPROM 26,("M","G","Z","7")
EEPROM 35,("U","F")
EEPROM 41,("i")			'i == INVITE
EEPROM 43,("K","C")
EEPROM 51,("W","P")
EEPROM 59,("O")
EEPROM 61,("8",":")
EEPROM 68,("V","u","]","$")	'u == UNDERSTOOD ]== EndOfWork
EEPROM 76,("X","/",0,0,0,0,0,0,0,"+",".")	'SLASH=77 PERIOD=86
EEPROM 92,("Q",0,".")
EEPROM 101,("!","?")		'QUESTION MARK=102
EEPROM 108,("Y","(",")")
EEPROM 116,("J")
EEPROM 125, ("9")
EEPROM 133, ("4")
EEPROM 158, (",")			' COMMA
EEPROM 161, (0,0,0,0,0,0,0,0,0,0,0,0,"[",";")	' > == STARTING
EEPROM 182, ("@")			' @ ==182
EEPROM 197, ("3")
EEPROM 200, ("A","B")
EEPROM 202, ("C","D","E","F")
EEPROM 206, ("G","H","I","J")
EEPROM 210, ("K","L","M","N")
EEPROM 214, ("O","P","Q","R")
EEPROM 218, ("S","T","U","V")
EEPROM 222, ("W","X","Y","Z")
EEPROM 226, (0,0,0,"2")
EEPROM 245, ("1","'")
EEPROM 253, ("0","'")

}
End
Here is a terminal session being echoed to the console as the command "C20" and "C21" come in via Morse Code.
08M2+ 32MHz Morse Control Test«0A»
C 2 0 Gosub C2:C2 LOW flush
C 2 1 Gosub C2:C2 HIGH flush
In the POC, I tied the C.2 port to a LED-resistor as a visual that the port did properly respond and would light the led (LOW) or extinguish the LED (HIGH).


This low speed signaling approach (approximately 5 - 8 WPM) is very slow, but should be reasonably reliable over some distances. Using simple filtering, low frequency hum and high frequency noise should be relatively easy to eliminate. The command queue is continuous recycling, so a distant PICAXE master unit can simply continue to send over and over to the receiver and only change state as required. Or, if the distant master unit does not check in, the remote PICAXE stays in-state until communication is re-established.

- Ray

IMG00366.jpg
Basic clipper circuit... do NOT pay attention to the PICAXE connection... on an 08M2, use PIN C.3 which is physical PIN#4
NPN inverter.jpg

- Up to 36+ devices chould share a single audio line if the first character were the "node address"
- Since Audio can be superimposed on DC, a 2-wire network could be created
- Works like UDP over TCP - no guarantee
 
Last edited:
Top