Saturday, 26 January 2013

Raspberry Whispers

After success with the "human readable" QRSS modes of FSK Morse and (my version of) "Hellschreiber", as seen here received by Steen Erik, la5goa up in arctic Norway,


I decided to add WSPR to my RPi Beacon's repertoire, to make it truly "multi-mode". I don't mean porting the WSPR code onto RPi (though doubtless this can be done - a Linux version is available). I mean cooking up my own WSPR signals in Python and transmitting them from inside the beacon previously described.

For anybody that doesn't know, WSPR is a true digital radio mode (in the sense that it is intended to be read by machines, rather than humans), developed by Joe Taylor, k1jt. I have enjoyed sending WSPR signals from my PIC-based multi-mode beacon - so I know the steps required. However, things have changed since the first time I trod the lonely path - so here's a description of how to do it today.

When I first played with generating WSPR signals, back in the summer of 2010, the secret was to use the command line version of k1jt's "WSPR" program. However, typing "WSPR" into a DOS command line these days just brings up the standard WSPR GUI. Fortunately, there's an alternative path...

The program "WSPRcode.exe" exists specifically to generate the WSPR message, which encodes the transmitting station's call sign, locator and power (in dBm). The locator is four symbols in the Maidenhead System which - for my home - is "IO83". The power is given in dBm (i.e. dB relative to 1 mW) and - as I'm producing around 200mW, I entered "23", making my message "m0xpd IO83 23".

Simply appending this string as an argument for the WSPRcode executable does the job...


The channel symbols describe which of the four frequencies which constitute the WSPR signal must be transmitted in each sequential interval. The WSPR "standard" tells us these frequencies must 1.4648Hz apart and that each interval lasts a little over 682 milliseconds. Also, a WSPR message must start 1 second into an even minute (that's to say the 0th, 2nd, 4th ... minute of the hour) in order to be received and understood.

It is clear that not only good timing - but accurate synchronisation to ABSOLUTE time is required.

Most WSPR transmissions come from computers, which keep good time (and may even be synchronised to time servers on the internet). Simple little beacons (like my PIC-based system) need to be started at precisely the top of the hour and keep good time thereafter. My PIC beacon hacked the timing framework developed by Gene Marcus, w3pm, who even included features required to synchronise to a GPS-derived timecode (though I haven't used that option recently).

In the case of the new Raspberry Pi beacon, timing remains the critical aspect of WSPR operation. Fortunately, unlike most PIC or AVR/Arduino alternatives, the RPi usually lives hooked up to the 'net, so absolute time is readily accessible. 

Here's the simple additional lines needed in my Python beacon code to transmit WSPR...

if Proceed == 1: # External timing code sets Proceed = 1 for WSPR
# and enters at the start of the transmission interval
sendFrequency(WSPR_freq) # setup the DDS frequency
GPIO.output(11, True) # key the Tx
time.sleep(1) # wait for 1 second : WSPR starts 1 second into the minute
for i in range(0,len(WSPR_Message)): # read each character
sendFrequency(WSPR_freq+WSPR_Message[i]*WSPR_df) # set frequency
time.sleep(0.682) # wait for 682 msec
GPIO.output(11, False) # All Done - so un-key the Tx
view raw WSPR_Tx.py hosted with ❤ by GitHub
 'WSPR_Message' is the vector of channel symbols (obtained from WSPRcode.exe, above). WSPR_freq is the desired transmission frequency and WSPR_df is the frequency spacing between the channels ( = 1.4648 Hz). My code has a main timing loop which waits for the start of each minute and calls either FSK-CW, Hellschreiber or (now) WSPR routines at the appropriate point in a 10-minute cycle, by setting the "Proceed" variable to non-zero value. Proceed = 1 initiates a WSPR transmission.

I tried the Raspberry-flavoured WSPR signal on the 30m band and was immediately spotted by the University of Twente's 'Experimentele TelecommunicatieGroep Drienerlo', pi4tht and by Jorgen, oz7it.

Here are the reports of these spots from the WSPRnet reporting system...

WSPRnet also has a map report...


Here's a snapshot of the map taken as I write, showing the last 24 hours of spots (the thickness of the line between me and a receiving station indicates the number of times that station reported receiving my signal - so it's a measure of the quality of the link to that receiver). The map includes the Raspberry Pi's best "dx" (distance) to date - the 2552 kilometers to Alexander, ua3arc's receiver in Moscow.


Please let me know if you detect a whiff of raspberry on 30 or 40 metres over the next few days

...-.- de m0xpd

Sunday, 20 January 2013

Multi-mode Beacon on the RPi

Inspired by some comments from Jim on Bill Meara's Soldersmoke Blog, I dusted off the m0xpd PayPal account (again!!!) and splashed out on one of the AD9850 DDS Modules from eBay...


Of course, you also need some kind of controller to make any use of these little synth modules. Fortunately, inspired by their beer-budget price, the modules are generating quite a buzz at the moment, so there's a lot of supporting software out there.

Andy Talbot, g4jnt, has written about the eBay modules in the current number of RadCom (but the dear old RSGB hasn't got around to updating their website at the time of blogging, so the link points to the previous edition - funny how the paper copy can be more up -to-date than electronic news!).

You can find Andy's PIC-based controller on-line - start with his description here .

Rather than follow the rest of the flock with a PIC controller (having already played with a PIC controller for DDS devices), I thought I'd like to use my new Raspberry Pi, so I had a further sniff about the 'net and found Ron, nr8o's blog, where he presents an Arduino sketch for setting up the AD9850.

I hacked that Arduino sketch into a piece of Python code for the Pi, which might be useful for others... 

#!/usr/local/bin/python
# Run an eBay AD9850 on the RPi GPIO
#
# translated from nr8o's Arduino sketch
# at http://nr8o.dhlpilotcentral.com/?p=83
#
# m0xpd
# shack.nasties 'at Gee Male dot com'
import RPi.GPIO as GPIO
# setup GPIO options...
GPIO.setmode(GPIO.BOARD)
GPIO.setwarnings(False)
W_CLK = 12 # Define GPIO pins
FQ_UD = 16
DATA = 18
RESET = 22
def pulseHigh(pin): # Function to send a pulse
GPIO.output(pin, True) # do it a few times to increase pulse width
GPIO.output(pin, True) # (easier than writing a delay loop!)
GPIO.output(pin, True)
GPIO.output(pin, False) # end of the pulse
return
def tfr_byte(data): # Function to send a byte by serial "bit-banging"
for i in range (0,8):
GPIO.output(DATA, data & 0x01) # Mask out LSB and put on GPIO pin "DATA"
pulseHigh(W_CLK) # pulse the clock line
data=data>>1 # Rotate right to get next bit
return
def sendFrequency(frequency): # Function to send frequency (assumes 125MHz xtal)
freq=long(frequency*4294967296/125000000)
for b in range (0,4):
tfr_byte(freq & 0xFF)
freq=freq>>8
tfr_byte(0x00)
pulseHigh(FQ_UD)
return
GPIO.setup(W_CLK, GPIO.OUT) # setup IO bits...
GPIO.setup(FQ_UD, GPIO.OUT) #
GPIO.setup(DATA, GPIO.OUT) #
GPIO.setup(RESET, GPIO.OUT) #
GPIO.output(W_CLK, False) # initialize everything to zero...
GPIO.output(FQ_UD, False)
GPIO.output(DATA, False)
GPIO.output(RESET, False)
pulseHigh(RESET) # start-up sequence...
pulseHigh(W_CLK)
pulseHigh(FQ_UD)
frequency = 7000000 # choose frequency and
sendFrequency(frequency) # start the oscillator
view raw AD9850.py hosted with ❤ by GitHub


Having got the synth to run, I wanted something to do with it - so I built up a "beacon" transmitter...


You can see (from the left) my GPIO breakout (here in the "High Rollers' edition"), then a quad bi-directional level converter module I made to couple the 3V3 signals from the RPi into the 5V world. Next there's the eBay module.

Its signal goes to a little QRP PA I knocked together (drawing on the PA stage in my "Funster Plus" rig) and a low-pass filter for 40m. Currently, I'm measuring 5.8V RMS at the output of the filter which (if we were to allow ourselves to be comforted by the delusion that the load thereafter is 50 Ohms) implies that I'm throwing a rather healthy 600mW at my half-g5rv. I'll throttle back to more sociable power levels when I get a chance.

Here's the amplifier...


and here's the filter...

See how my obsession with Plugin Modules is getting worse!

With the work I'd already done on sending Morse Code from the RPi, it was easy to get the system to send my call (this time at QRSS speeds, with a 3 second dit length).

Here's the first spot (in the red box) of my transmissions at pa9qv's "Double Dutch" grabber...


With all the processing resource of the RPi, it was also easy to transmit Hellschreiber messages making this a multi-mode beacon (in the spirit of my original).

Here's my multi-mode signal arriving in the Netherlands...


I've added the annotation in red to help you see it! I only transmitted the rather flamboyant "Raspberry Pi" element of the message for fun. After the buzz subsides, I'll go back to sending just my call!

I guess the next step is to get the RPi beacon to send WSPR as well - but first, I must get onto 30m - there are more grabbers on that band - so more chance of seeing my Raspberry Pi proclaim itself to a waiting world!.

 ...-.- de m0xpd

Update :-

The beacon is now even more "multi-mode" than before ... in addition to FSK-Morse and "Hellschreiber", it now also transmits the digital "WSPR" mode.

Friday, 18 January 2013

Rotary Encoders on RPi

I've been playing with an incremental rotary encoder on the Raspberry Pi - for no better reason than the mountaineers' lame excuse "because it's there"...


The incremental rotary encoder produces two quadrature square waves (as you can learn by reading any of a number of descriptions including this one). By detecting the relative phase of the square waves, you can figure the direction of rotation and (if you are really keen to encode angular velocity accurately) the speed of rotation too.

I settled for a clumsy attempt to decode direction alone, using this circuit...


Of course, you need some code too - I wrote the following simple demonstration in Python. It detects positive-going transitions on "PinA" and looks at the state of PinB during these transition events to figure the direction in which the encoder has been moved.


#!/usr/local/bin/python
# m0xpd
# shack.nasties 'at Gee Male dot com'
import RPi.GPIO as GPIO
# setup GPIO options...
GPIO.setmode(GPIO.BOARD)
GPIO.setwarnings(False)
# setup IO bits...
GPIO.setup(12, GPIO.IN) # PinA is on GPIO 12
GPIO.setup(16, GPIO.IN) # PinB is on GPIO 16
# check initial state...
oldPinA = GPIO.input(12)
Position = 0
inc = 5 # increment
REmax = 127 # Max value
REmin = 0 # Min value
# Main Operating Loop...
while True:
encoderPinA=GPIO.input(12)
encoderPinB=GPIO.input(16)
if (encoderPinA == True) and (oldPinA == False): # Detect "upward" transition on PinA
if (encoderPinB == False): # if PinB is low...
Position=Position+inc # we're going clokwise
if Position > REmax: # limit max value
Position = REmax
else: # otherwise...
Position=Position-inc # we're going anti-clockwise
if Position < REmin: # limit min value
Position = REmin
print Position
oldPinA=encoderPinA # save current value for next time

After the fun-and-games with the MIDI controller, I'm really excited about object-oriented software for the RPi, so here's an elaboration which includes a class for Rotary Encoders, connected to any two GPIO pins on the RPi. There's also facility to read a push-button (which some encoders - including mine - feature).


#!/usr/local/bin/python
# m0xpd
# shack.nasties 'at Gee Male dot com'
import RPi.GPIO as GPIO
# setup GPIO options...
GPIO.setmode(GPIO.BOARD)
GPIO.setwarnings(False)
class RotaryEnc:
'Base Class for Rotary Encoder on the RPi GPIO Pins'
def __init__(self,PinA,PinB,button,Position,REmin,REmax,inc):
self.PinA = PinA # GPIO Pin for encoder PinA
self.PinB = PinB # GPIO Pin for encoder PinB
self.button = button # GPIO Pin for encoder push button
self.Position = Position # encoder 'position'
self.min = REmin # Max value
self.max = REmax # Min value
self.inc = inc # increment
self.old_button = 1 # start value for previous button state
self.oldPinA = 1 # start value for previous PinA state
self.button_release = 0 # initialise outputs
self.button_down = 0 # initialise outputs
GPIO.setup(self.PinA, GPIO.IN) # setup IO bits...
GPIO.setup(self.PinB, GPIO.IN) #
GPIO.setup(self.button, GPIO.IN) #
#
def read(self): # Function to Read encoder...
encoderPinA=GPIO.input(self.PinA) # get inputs...
encoderPinB=GPIO.input(self.PinB) #
if encoderPinA and not self.oldPinA: # Transition on PinA?
if not encoderPinB: # Yes: is PinB High?
self.Position=self.Position+self.inc # No - so we're going clockwise
if self.Position > self.max: # limit maximum value
self.Position = self.max #
#
else: #
#
self.Position=self.Position-self.inc # Yes - so we're going anti-clockwise
if self.Position < self.min: # limit minimum value
self.Position = self.min #
self.oldPinA=encoderPinA # No: just save current PinA state for next time
#
def read_button(self): # Function to Read encoder button...
button=GPIO.input(self.button) # get input...
if button and not self.old_button: # 'Upward' transition on button?
self.button_release=1 # Yes - so set release output
else: #
self.button_release=0 # No - so clear it
if not button and self.old_button: # 'Downward' transition on button?
self.button_down = 1 # Yes - so set 'down' button
else: #
self.button_down = 0 # No - so clear it
self.old_button=button # Save current button state for next time
# -------------------------------------------------------------------------------------------------------------------
# Here's a simple example of how to use the RotaryEnc Class ...
#
# Set up Rotary Encoder...
# format is : RotaryEnc(PinA,PinB,button,Position,REmin,REmax,inc):
Position = 1 # Position is also used as a variable
RE1 = RotaryEnc(12,16,18,Position,0,10,1) # Instatiate the encoder
# Main Operating Loop...
while True:
RE1.read() # Read the encoder
if Position <> RE1.Position: # has the position changed?
print RE1.Position # Yes: so print new value
Position = RE1.Position # and update it
RE1.read_button() # Read the button
if RE1.button_down: # Has it been pressed?
print 'Button Pressed' # Yes: so print a message
if RE1.button_release: # Has it been released
print 'Button Released' # Yes: so print a message
Here's a screen shot showing me running the second program ...


The code works - but only just...

It is important to sample a rotary encoder at a sufficiently high sample rate to avoid aliasing which - in this case - results in missed "moves" or even moves of incorrect direction.

I cannot get the RPi to sample fast enough to guarantee that every movement is perfectly observed, whether in the "while" loop seen in the programs above, or in a tkinter ".after()" command with a delay argument of 1 millisecond. What's worse, the little graph of processor utilisation at the bottom right of the RPi desktop (like the PC's "Task Manager" or the Mac's "Activity Monitor") reveals that the processor is working hard to sample the encoder at this rate (as you can just see in the screenshot above). I don't know enough about RPi to know if an interrupt-driven approach, such as would be easy and obvious on a PIC, is possible or desirable.

Having said all that, the code above works well enough to let me add a "knob" to my simple RPi MIDI controller, making it much more user-friendly than the original mouse drag-able sliders.

Next, I hope to use the rotary encoder to control the frequency of the DDS generator module from eBay that's just arrived on my doormat - watch this space!

...-.- de m0xpd

Monday, 14 January 2013

RPi GPIO Breakout for High Rollers

Having described the initial low rent version, I'm pleased to announce the arrival of an upmarket edition of my simple connector...


Notice how I've dodged the possibility of inadvertently connecting something unhealthy to the proscribed pins by the simple expedient of pulling out the corresponding pins from my device. I couldn't now "connect" if I wanted to!

The Cheapskate's version might not look so classy, but it wins hands down - drilling fifty two holes is a chore!

 ...-.- de m0xpd

Friday, 11 January 2013

MIDI Controller on the RPi

Here's the story of my attempts to squeeze MIDI out of my new RPi. The How To's and Gotcha's concerning configuration, the hardware interface details and the full code examples make it something of a tutorial, which will allow others to build their own Raspberry-flavoured MIDI controller (should they be foolish enough to try)…


It started with my newly-MIDIfied Tube Screamer, which was all ready waiting for a controller. Of course I could continue to generate control signals in MIDI-OX, but I wanted to make something a little more specific to the Tube Screamer. Sat there on the bench was my new RPi waiting for a new application - so why not kill two birds with one stone? 

Unfortunately, the RPi can't generate MIDI straight out of the box, for a few good reasons;
  • the UART is doing something else by default - it's configured to serve up Linux Kernel messages
  • the UART doesn't offer the correct baud rate for MIDI serial comms
  • MIDI needs a hardware interface layer to implement the current loop
Fortunately, all these problem can be overcome, more-or-less simply…

In what follows, I'm referring to the standard, "Mini UART" accessible on the GPIO pins. There are dark whispers about a second, more flexible UART buried within the Broadcom device, but that's not for newbies like us. Also, there is the possibility of plugging in a USB - MIDI device to one of the USB sockets but that's not in the hack/knack spirit, so we're staying with the simple UART…

First job is to kill off the messages squirting out of the serial port by default. You need to edit the cmdline.txt file to remove (that's to say delete) the parameter:

console=ttyAMA0,115200 kgdboc=ttyAMA0,115200

Easy.

Next, you'll also want to disable the serial login ("getty") in /etc/inittab, by commenting out the line similar to this one:

T0:23:respawn:/sbin/getty -L ttyAMA0 115200 vt100

just by prefixing it with a hash.

After a reboot, you shouldn't have any more unwanted stuff squirting out of your Mini-UART Tx pin. There's any number of detailed tutorials explaining how to achieve the steps above, including this one .

Next, you need some software to allow you to interact with the UART - I was intending to program in Python, so I installed the pySerial module, which is available for download here as a tar.

Once you have pyserial installed, you can try to use the UART to send MIDI data - only you'll hit a problem…

The Mini-UART only supports a few baud rates and the MIDI rate of 31250 is not one of the available set! If you set the baud rate to 31250, it will send serial data happily - but at a higher baud rate (of 38400 - the nearest available "standard" value).

Fortunately, this is a mountain that has been climbed by others before us, so there's a trick that we can use…

We can fool the UART into sending serial data at the correct speed by changing its master clock. If we saw our intended 31250 baud data actually being output at 38400, that is an error ratio of 38400/31250 - it is sending 23% fast. So , if we reduce the master clock speed by the same ratio, everything should be fine. 

There's opportunity to edit the config.txt file by adding a line of form…

init_uart_clock = 3000000

This line is shown above in the default clock rate of 3 MHz - we can change it (by taking 3MHz * 31250/38400 = 2441406) to:

init_uart_clock = 2441406

I did it and - guess what - it did ABSOLUTELY NOTHING! The serial data was still pumping out at 38400.

It turns out that you also need to add another parameter to cmdline.txt, as taught by Dom in this important post. Dom, who's a moderator at RaspberryPi.org and obviously knows what he's doing, suggests

"Can you try adding :

bcm2708.uart_clock=3000000

to cmdline.txt"

I tried and - guess what - is still did ABSOLUTELY NOTHING! The serial data was STILL pumping out resolutely at 38400 baud.

I thought about it (for quite a long time, involving caffeine, alcohol etc) and eventually started to wonder if there was a clue lurking in the filename 'cmdline.txt' …

I wondered if the addition had to be part of ONE LINE, rather than just part of a file. Sure enough, it does - adding Dom's suggestion to the ONE LINE in cmdline.txt suddenly made everything work as intended - serial was being produced at 31250 baud.

I can hear all the Linux experts laughing at me. But, in truth, the joke is on them - this is a piece of legacy code from the days when Linux/Unix expected me to be a bearded, cardigan-wearing geek, typing lines on a serial console. OK - so I have a beard. OK - so I often wear a cardigan. Linux still includes some dumb, laughable, pre-historic features.

Now we've got the UART running, we need some code to generate MIDI. I'll show you three species of increasing complexity.

First, a simple command-line Python program …

#!/usr/bin/env python
# m0xpd
# shack.nasties 'at Gee Male dot com'
import serial # Get the Serial library
ser=serial.Serial('/dev/ttyAMA0',38400) # Set up the UART
channel=int(raw_input("Enter MIDI Channel :")) # input the desired MIDI Channel
command=0xB0 + channel -1 # Build the command byte
controller=int(raw_input("Enter Controller :")) # input the controller number
while True: # set up a loop...
value=int(raw_input("Enter Value :")) # Get the controller value
accept=raw_input("Sending " + hex(command) + " " + hex(controller) + " " + hex(value) + " OK ? ('y' to send) ")
if accept=='y': # Happy with this ?
message=chr(command) + chr(controller) + chr(value)
ser.write(message) # then transmit the message

This program prompts the user for a MIDI channel and a controller number, identifying a unique controller. It will pass a sequence of values to this controller (values are like function arguments for the controller), until you break the program with a "ctrl-c" input from the keyboard.

Here's a screenshot of my console when running it…


In the case above, I chose the MIDI channel 9 and the controller 12, which corresponds to the "Level" control of my Tube Screamer (I arbitrarily assigned these values in writing the PIC code for the Tube Screamer).

The Control Change Message format contains three bytes; the first has 0xB as its high nibble and the channel number-1 as its low nibble. 0xBn is the code for "Control Change" on channel n+1 and the channel can run from 1 to 16, so channel-1 (=n)  will fit into the lower nibble's four bits. The second byte is the controller number, which can run from 1 to 127, encrypted as the seven least significant bits - in my case this is 12 (0x0C). The final byte is the value selected. In the screenshot above, I entered two values; 20 and 127.

 Here's the signal that appears on the UART Tx pin (GPIO 8) for the first Control Change Message (value = d'20' = 0x14)…


You can see that MIDI message bytes have a "0" start bit and a '1' stop bit. Also, the data is transmitted LSB first, so 0xB8 (=b'10111000') appears reversed as b'00011101 (=0x1D) etc. You can also see that the signal is switching between 0 and 3.3 volts - we need some hardware…

MIDI is a serial protocol, much like RS232. It uses a current loop to transmit and receive data and we need to ensure that we are capable of sending the correct currents, without damage to the RPi. Luckily, it's easy - the current required is just a few milliAmps, having only to light an LED in the receiving device's opto-isolator - we all know that the GPIO pins of a RPi are well able to turn LEDs on and off! This implies that we just need a current-limiting resistor and an appropriate connector (which, for MIDI, is a 180 degree 5-pin DIN socket). However, we'll be a little more "proper" than that and transpose to the correct working voltages using a level converter to switch from the RPi's native 3.3 volt signals to the more common 5V.

In my case a 74HCT14 was staring at me from the "junk box", so I pressed it into service, just as others have done before me


You can see the realisation of this interface in the photo at the top of this post.

At this point, having confirmed correct operation of the command-line interface program above, I decided to try to build a GUI interface, so I installed 'tkinter' (other fine GUI programming toolkits for Python are [no doubt] available) and started to learn something about GUI programming in Python. It is easy!

Here's a little program that generates a GUI to send data to a single MIDI control in response to movement of a slider…

 
#!/usr/bin/python
# m0xpd
# shack.nasties 'at Gee Male dot com'
import sys, serial # get libraries for Serial
from Tkinter import * # and GUI
ser=serial.Serial('/dev/ttyAMA0',38400) # set up the UART
channel=9 # Set MIDI details
Level_controller = 12 # (change for your application)
command=0xB0 + channel - 1 # Build the Control Change Byte
def set_level(x): # A function to set the control value
y=float(x)
value=int(y*127/100)
message=chr(command) + chr(Level_controller) + chr(value)
ser.write(message) # and send the message
def cleanup(): # A function to close the GUI
root.destroy()
root = Tk() # Now we set up the GUI,
root.wm_title(sys.argv[0]) # set title
root.wm_protocol("WM_DELETE_WINDOW", cleanup) # and specify closing behaviour
# Now specify the slider widget...
s = Scale( root, command=set_level, orient=HORIZONTAL, length=300, label='Level', from_=0, to=100)
s.pack(anchor=CENTER) # and add it to the window
root.mainloop() # Loop round, sending MIDI when the slider is moved

The comments in the program listing should be sufficient for you to figure out how it works - here's how it looks when it runs (you open the program from the terminal and the GUI window appears)…


This is all very well - but most MIDI devices have more than one control (why, even a simple device like the MIDI Tube Screamer has four!). This ought to be reflected in the controller - so we need to think about how to work with number of controls. Hang on a minute - that sounds like an opportunity to get involved in some "Object-Oriented Programming", which is just the sort of thing Python ought to be good at. Let's see if we can make an object-oriented controller…

 The Tube Screamer has three controls which take input argument 0:127. These "continuous" controls (which are called "Drive", "Tone" and "Level") correspond to the three potentiometers on the original device (and on Dolly the clone). I assigned control numbers 13,14 and 12 to these controls in my PIC code.

There's another control - associated with the footswitch - which switches the effect On or Off (actually, its on all the time - it just bypasses the effect for "off"). This "two-state" or "Boolean" control really only needs a single bit input - but the MIDI Control Change Message doesn't support this, so we still take the wasteful approach of sending a whole byte. Some controllers interpret 127 as on and 0 as off (or vice-versa). Heaven knows what they do if they receive anything other than 127 or 0!

I took the approach of interpreting anything greater than 63 as "On" and anything less than 63 as "Off" (in other words I just look at the highest bit of the seven bit "value"). These "continuous" and "Boolean" controllers sound rather different - but they can both be handled as instances of one class of MIDI controller...

#!/usr/bin/python
# m0xpd
# shack.nasties 'at Gee Male dot com'
import sys, serial # get libraries for Serial
from Tkinter import * # and GUI
ser=serial.Serial('/dev/ttyAMA0',38400) # set up the UART
# This time, we're using an Object-Oriented approach,
# so we define a new class of MIDI Controls...
class MIDIControl:
'Base Class for MIDI Control Changes'
def __init__(self, channel, control, value): # MIDIControls have...
self.channel = channel # a channel, from which they build...
self.command=0xB0 + self.channel - 1 # ...a 'Control Change' command
self.control = control # to send to a specific control 'address'
self.value = value # to which they pass a 'value' operand
# MIDIControls also have
def modify(self,x): # a method to modify value
y=float(x)
self.value=int(y*127/100)
self.send() # and send it
def toggle(self): # a method to toggle their value
if self.value>63: # (used for Boolean 'On/Off' controls)
self.value=0
else:
self.value=127
self.send()
def send(self): # a method to send the CC Message
message=chr(self.command) + chr(self.control) + chr(self.value)
ser.write(message)
def cleanup(): # Function to close the GUI
root.destroy()
Drive = MIDIControl(9, 13, 25) # Instantiate some MIDIcontrols...
Level = MIDIControl(9, 12, 50) # (these are for my Digital "Tube Screamer" project...
Tone = MIDIControl(9, 14, 50) # change these controller definitions to suit your project)
Bypass = MIDIControl(9, 15, 0)
root = Tk() # set up the GUI...
root.geometry("500x250+400+400") # Control the size,
root.wm_title(sys.argv[0]) # set title,
root.wm_protocol("WM_DELETE_WINDOW", cleanup) # and specify closing behaviour
# Now specify three slider widgets...
s1 = Scale( root, command=Drive.modify, orient=HORIZONTAL, length=300, label='Drive')
s1.set(Drive.value) # setup (and send) start value
s1.pack(anchor=CENTER) # and add to the window
s2 = Scale( root, command=Tone.modify, orient=HORIZONTAL, length=300, label='Tone')
s2.set(Tone.value)
s2.pack(anchor=CENTER)
s3 = Scale( root, command=Level.modify, orient=HORIZONTAL, length=300, label='Level', from_=0, to=100)
s3.set(Level.value)
s3.pack(anchor=CENTER)
# Now specify a button widget...
v=IntVar() # it needs a variable to track state
b1 = Checkbutton( root, text="ON / Off", command=Bypass.toggle, variable=v, pady=30)
b1.var=v
Bypass.send() # force a send of the start value
b1.pack(anchor=CENTER) # and add button to the window
root.mainloop() # Loop round, sending MIDI when requested
Here's the resulting GUI, offering remote access to all the MIDI Tube Screamer's controls...

With the object-oriented structure it is easy to set up more instances of controllers - and GUI widgets to operate them, whether sliders or buttons. In fact, the greatest problem is arranging the widgets in the window!

Let me know if you construct any MIDI applications using these building blocks.

Today I ordered a DDS module hosting an AD 9850 chip - perhaps the next RPi application will be at RF rather than AF !

 ...-.- de m0xpd

Sunday, 6 January 2013

RPi GPIO Breakout for Cheapskates

I told you in an earlier post how much I admire Adafriut's Pi T-Cobbler breakout for the Raspberry Pi. I got as far as finding a UK reseller who listed them (for more GB Pounds than Adafruit charge in US Dollars) but they're on long-term back order - good thing, given the price! I resorted to my native parsimony...

I soon had a copper pattern laid out for a "straight" version which, although it didn't have the elegance of the Adafruit "T" configuration, was (like the T-Cobbler) only 0.3 inches between pin rows, thereby preserving as much breadboard area as possible.


Unfortunately, the XYL smashed my nice old laboratory thermometer over the holidays - serves me right for leaving it in the kitchen (she says). The thermometer is important in my new photo PCB process (to check the temperature of the photoresist developer), so PCB processing is on hold until the replacement arrives from eBay. Undaunted, I looked around for alternatives...

The junkbox yielded up a scrap of stripboard and I stock single and double row headers, so I quickly collected the key ingredients...


I cut the break in the copper tracks using a dental burr in my pre-historic horizontal milling machine, but I'm sure you could do the same job with a knife (it is between holes, you see - so you can't rely on the usual track cutter / drill). A little judicious soldering (after forceful re-arrangement of the pins in the single row headers to make best use of available length) and you have it...


Here's the new baby on the breadboard (still hosting the LED for my Morse Code GMail Notifier and test buttons for my RPi Keyer)...


All that's needed now is a ribbon cable and some IDC connectors - fortunately, the junkbox came up trumps again, serving up an old IDE disk cable...


However, that cable is bigger than the Pi and the breadboard together, so I broke the habit of a lifetime and dusted off the m0xpd PayPal account to order something more appropriate (£2.99, delivered in moments, from PC Supplies Limited - usual disclaimer)


Now the whole shebang looks a lot neater...


Next I've got to figure how to get the UART to send MIDI to my latest monstrous creation at 31250 baud - the widely touted solution of  suppressing the console UART (in cmdline.txt), getting rid of the getty (in inittab.tcxt) and modifying the UART clock speed with a line in config.txt of the form:

init_uart_clock=2441406 

isn't working for me. In fact, it isn't doing anything - serial is coming out resolutely at 38400 baud.

Any reader who sends a solution ( to shack.nasties "at gee male dot com") will get a prize. A very small prize of zero monetary value and no physical substance - but a prize none the less!

Update: The competition is closed - and I claimed the prize (I'm beginning to regret the "zero monetary value" status). I now have the RPi sending MIDI and controlling my MIDI Tube Screamer - I'll tell you all about it in a later post

 ...-.- de m0xpd

Saturday, 5 January 2013

MIDIfied Tube Screamer

My Tube Screamer clone (Blogs passim) now sports a MIDI interface and can be controlled externally by MIDI "Control Change" messages...


I've played with sending MIDI from PICs a lot - that being the essence of my Virtual Organ Project; both in generating Note On / Note off messages from my MIDI bass pedals and various Control Change messages from organ drawbars, switches and other controls. But MIDI Tx is easy. It turns out that receiving MIDI is only slightly more tricky but interpreting and acting on MIDI messages is a whole lot tougher!

In the aerial photograph above, you can see the new MIDI elements at top right. There's another Breadboard Module with the obligatory 5-pin 180 degree DIN socket. There's also a little bit of electronics to squeeze MIDI into the PIC. MIDI is a current loop interface and needs an opto-isolator to terminate the Rx end correctly. I had some 4N25s in the junk box, but couldn't get these to work sweetly. Fortunately, I also had some CNW 136s and these worked very well.

Here's my MIDI Rx schematic...


After this simple circuit, the MIDI signal goes to the Rx pin on the 16F887 at the heart of my Digital TS. Of course, masochists can use bit-banging methods to receive the serial MIDI data (just as I continue to use Ross Bencina's code for sending MIDI in the Virtual Organ). However, for MIDI reception, you really need to use the PIC's UART (or EUSART, as Microchip insist on calling it).

There are any number of experts out there who will explain how to set up the UART for MIDI reception (standard 8-bit at 31250 baud). They will have you looking at the RCIF flag bit of the PIR1 register to see when data has arrived. You'll soon appreciate that you need to do this in an interrupt service routine, such that you can get on and do other things in the meantime (such as servicing the local control interface with its buttons and display). After some messing around, you'll get a MIDI stream into your controller - but what do you do with it?

The hard part isn't the reception of MIDI data (despite what you may think if you're struggling to get it going). The hard part is making your system react to it. Here's how I went about it...

Currently I have four "controls" in the digital tube screamer, matching exactly the user controls of the original guitar effect pedal. These controls are 'Drive', 'Tone', 'Level' and 'Bypass'. I want to be able to send values for each of these controls via MIDI from a control device.

MIDI has a feature exactly matched to this requirement (no surprise, since this is exactly what MIDI was conceived for), called "Control Change Messages". These messages identify a particular control on a particular MIDI channel (together constituting an "address" for that control) and pass it a numerical value (an integer between 0 and 127). It is the job of the receiving hardware to understand the message and to apply the value to the control (equivalent in this case to setting the position of one of the knobs on the Tube Screamer or operating its Bypass switch).

Here's my interrupt service routine which does the job...
;=====================================================================================
; PIC 16f887 (etc) Interrupt Service Routine
; to parse a MIDI stream for control change messages of format...
; b'1011nnnn', b'0ccccccc', b'0vvvvvvv'
; where
; nnnn is the MIDI channel number-1 (0:15)
; ccccccc is the control number (0:127)
; vvvvvvv is the control value (0:127)
;
; MIDIST is a byte of flags indicating MIDI State
; MIDI_Ch is the MIDI channel-1 (nnnn), stored as a variable
; LvlCtl, TonCtl, DrvCtl & BypCtl are control numbers (ccccccc), defined as constants
;
; These controls were specific to my MIDI Tube Screamer Application -
; you can modify / add to suit your own project
;
; m0xpd
; shack.nasties "at gee male dot" com
; http://www.m0xpd.blogspot.com
; January 2013
;
; Terms of use:
; Tell me if you like it,
; Mention me if you use it,
; Thank me if you sell it,
; Don't blame me if it doesn't work.
;=====================================================================================
IntSR
movwf _w ; Save the Current Values
swapf STATUS, w
movwf _status
bcf STATUS, RP0 ; Make Sure We're in Bank 0
;
call MIDIRx ; get MIDI byte (saved in RX_TEMP)
;
;==================================================================
; Parse MIDI Stream for valid Control Change message...
;==================================================================
; Check the MIDIST Byte...
btfsc MIDIST,7 ; was last MIDI byte a Control Change?
goto Control ; YES - so go deal with it
; NO - but is THIS byte Control Change?
movf MIDI_Ch,w ; get the MIDI Channel
addlw 0xB0 ; add the Control Change prefix
subwf RX_TEMP ; compare to the current MIDI byte...
btfss STATUS,Z ; is it CC on the correct Channel?
goto NoCC ; No it isn't
clrf MIDIST ; Yes, so put 0x80 into MIDIST
bsf MIDIST,7 ; to set the "CC" Flag
goto Idone ; and wait 'til next byte
NoCC
clrf MIDIST ; Not a CC - so reset MIDI state
goto Idone ; and wait for next byte
Control
movf MIDIST,w ; If we get here, CC has been set...
andlw 0x7F ; is any other flag set?
btfss STATUS,Z
goto setMIDIControl ; Yes - so this byte is the
; control value - go deal with it
movf RX_TEMP,w ; No - see if byte is a valid Control...
sublw LvlCtl
btfss STATUS,Z ; is it Level ?
goto Control2 ; No - try next...
bsf MIDIST,6 ; Yes - set Level Flag
goto Idone ; and wait til next MIDI byte
Control2
movf RX_TEMP,w
sublw DrvCtl
btfss STATUS,Z ; is it Drive ?
goto Control3 ; No - try next...
bsf MIDIST,5 ; Yes - set Drive Flag
goto Idone ; and wait til next MIDI byte
Control3
movf RX_TEMP,w
sublw TonCtl
btfss STATUS,Z ; is it Tone ?
goto Control4 ; No - try next...
bsf MIDIST,4 ; Yes - set Tone Flag
goto Idone ; and wait til next MIDI byte
Control4
movf RX_TEMP,w
sublw BypCtl
btfss STATUS,Z ; is it Bypass ?
goto ControlFail ; No - give up...
bsf MIDIST,3 ; Yes - set Tone Flag
goto Idone ; and wait til next MIDI byte
;
; add in code to handle three more Flags in MIDIST here, if needed
; or use one of them to handle a System Exclusive message
;
ControlFail
clrf MIDIST ; No valid control received
goto Idone ; so reset and wait...
setMIDIControl
; We have a Control Flag set -
; find which one it is...
btfsc MIDIST,6 ; it is Level?
goto setMIDILevel ;
btfsc MIDIST,5 ; it is Drive?
goto setMIDIDrive
btfsc MIDIST,4 ; it is Tone?
goto setMIDITone
btfsc MIDIST,3 ; it is Bypass?
goto setMIDIBypass
setMIDIControlFail
clrf MIDIST ; No valid control received
goto Idone ; so reset and wait...
setMIDILevel
; Set the Level control
; Change these lines to do
; something different in your application
; but make sure it doesn't take too long
; or you may miss the next MIDI byte
movf RX_TEMP,w
movwf M3_Opt
call SetLevl
; If you changed stuff, pick up here...
clrf MIDIST ; clear MIDI State
goto Idone ; and exit
setMIDIDrive
; Set the Drive control
movf RX_TEMP,w
movwf M1_Opt
call SetDrive
clrf MIDIST ; clear MIDI State
goto Idone ; and exit
setMIDITone
; Set the Tone control
movf RX_TEMP,w
movwf M2_Opt
call SetTone
clrf MIDIST ; clear MIDI State
goto Idone ; and exit
setMIDIBypass
; Set the Bypass State
movf RX_TEMP,w
btfsc RX_TEMP,6 ; If control value >63
goto Turn_On ; branch
; otherwise
bsd 0x03 ; turn effect off
; 'bsd' and 'bcd' are MACROS which set and clear
; PORTD bits safely
; (DON'T use bsf or bcf on a PORT!!!)
goto setMIDIBypass_Done
Turn_On
bcd 0x03 ; turn effect on
setMIDIBypass_Done
clrf MIDIST ; clear MIDI State
goto Idone ; and exit
Idone
swapf _status, w ; Return from Interrupt with Valid Regs
movwf STATUS
swapf _w
swapf _w, w
retfie
;
; End of Interrupt Service Routine
;========================================================================
view raw PIC MIDI Parser hosted with ❤ by GitHub
You can modify this code to parse a MIDI stream for your own application - the only thing it doesn't (try to) do at the moment is handle "System Exclusive" messages. In my case, I'm happy to take the (very small) risk that one such message might mess with the controls of my Tube Screamer.

I tested my MIDIfied Tube Screamer by generating MIDI using the superb MIDI-OX software. I guess the next step is to make a little controller to replace the computer and MIDI-OX.

Wait a minute - wouldn't it have been easier to just put knobs on the Tube Screamer? Now there's a thought...

 ...-.- de m0xpd