Archive for November, 2008

Focused Workshop, 30Nov08, Making Noise with the Arduino.

Wednesday, November 19th, 2008

http://farm4.static.flickr.com/3208/3043303516_20722aebfd.jpg

As a part of the ongoing Arduino Cult Induction workshop series, this month we will be focusing on sound.

In particular I will be going over creating sound using the Arduino’s built in Pulse Width Modulation (PWM) and Direct Digital Synthesis using resistive ladders. We will investigate using the Piezo element
as both a simple speaker and an input trigger. We will review the Musical Instrument Digital Interface (MIDI) from a hardware and software perspective, and look at a couple of useful integrated
circuits (the lm324 and the lm368).

The workshop will cost $25 and participants will recieve a complete midi interface board for the dorkboard/arduino and a piezo element which will be used to create a midi drum trigger.
Participants will also take with them parts for an r2r ladder, an op amp and an audio amp capable of driving small speakers or headphones.

Participants should leave the workshop with a better understanding of how micro-controllers such as the Arduino can be used to create and control sound, they should have built at least one working musical controller and they should have the foundations for several sound related projects.

Please bring your dorkboard, rbba, or other arduino compatible board, a soldering iron or breadboard, and a laptop.

The workshop will be held at PNCA (NW 12th and Johnson) in room 205 from 1-5pm on Sunday, November 30.

To reserve a space you can paypal the workshop fee to cult@tempusdictum.com and you can feel free to email me with any questions.

The Arduino Cult Induction series of workshops are Sponsored by [http://www.tempusdictum.com/ Tempus Dictum, Inc]. in support of [http://www.dorkbotpdx.org DorkbotPDX]. (graphic by [http://www.noisybox.net Jason Plumb] )

Build Your own open source usb/midi gateway

Saturday, November 15th, 2008

I am trying to digest this document.

http://www.usb.org/developers/devclass_docs/midi10.pdf

USB encapsulates midi data into 4 byte chunks and adds the notion of virtual cables.
The first byte in the packet is the cable number and something called the Code Index Number. The remaining 3 bytes are either midi or padding depending on the packet.

Most of the relevant midi status commands have a corresponding Code Index Number.

USB Midi Codes with 1to1 Midi Status Mapping
c=virtual cable,n=midi channel
CIN Status Bytes Description
0xc8 0x8n 2 Note ON
0xc9 0x9n 2 Note OFF
0xcA 0xAn 2 Key Pressure
0xcB 0xBn 2 Control Change
0xcC 0xCn 1 Program Change
0xcD 0xDn 1 Channel Pressure
0xcE 0xEx 2 Pitch Wheel

The rest of it is a mess of what to do with the odd sysex bytes. To pass the input stream to midi should look something like this.

/* Select the Stream Out Endpoint */
Endpoint_SelectEndpoint(MIDI_STREAM_OUT_EPNUM);

if (Endpoint_ReadWriteAllowed())
{
	/* Read the recieved data endpoint into the transmission buffer */
	while (Endpoint_BytesInEndpoint())
	{   cin=Endpoint_Read_Byte();
		midibyte[0]=Endpoint_Read_Byte(); // should probably do some sort of checking here.
		midibyte[1]=Endpoint_Read_Byte(); // should probably do some sort of checking here.
		midibyte[2]=Endpoint_Read_Byte(); // should probably do some sort of checking here.
		i=0;
		switch (cin&0x0F) { //strip off cable data.
			case CIN_3BYTE_SYSTEM_COMMON:
			case CIN_SYSEX_ENDS_IN_THREE:
			case CIN_NOTE_ON:
			case CIN_NOTE_OFF:
			case CIN_PRESSURE:
			case CIN_SYSEX_CONTINUES:
			case CIN_PITCH_WHEEL:
			case CIN_CONTROL_CHANGE:
				while (!((BUFF_STATICSIZE - Rx_Buffer.Elements)));
				Buffer_StoreElement(&Rx_Buffer,midibyte[i++]);
			case CIN_SYSEX_ENDS_IN_TWO:
			case CIN_2BYTE_SYSTEM_COMMON:
			case CIN_PROGRAM_CHANGE:
			case CIN_CHANNEL_PRESSURE:
				while (!((BUFF_STATICSIZE - Rx_Buffer.Elements)));
				Buffer_StoreElement(&Rx_Buffer,midibyte[i++]);
			case CIN_SYSEX_ENDS_IN_ONE:
			case CIN_SYSTEM_1BYTE:
				while (!((BUFF_STATICSIZE - Rx_Buffer.Elements)));
				Buffer_StoreElement(&Rx_Buffer,midibyte[i++]);
			case CIN_RESERVED_FUNCTION_CODES:
			case CIN_RESERVED_CABLE_EVENTS:
				break;
		}
		Flash_RXD(); // do the pov thing
	}

	/* Clear the endpoint buffer */
	Endpoint_ClearCurrentBank();
}


In other words since the midi data is complete in the packets. You just need to determine how many of the 4 bytes to send and strip off the cable information.

Going the other way is a little more convoluted.

void ParseMidiByte(const uint8_t theByte) {
    if (theByte&0x80) {
		thePacketIndex=0;
		switch (theByte&0xF0) {
			case MIDI_STATUS_NOTE_ON:
			case MIDI_STATUS_NOTE_OFF:
			case MIDI_STATUS_PRESSURE:
			case MIDI_STATUS_CONTROL_CHANGE:
			case MIDI_STATUS_PITCH_WHEEL:
				thePacket[thePacketIndex++] = theCable|(theByte>>4);
				thePacket[thePacketIndex++] = theByte;
				theMidiState = midi_state_needs_2bytes;
				break;
			case MIDI_STATUS_PROGRAM_CHANGE:
			case MIDI_STATUS_CHANNEL_PRESSURE:
				thePacket[thePacketIndex++] = theCable|(theByte>>4);
				thePacket[thePacketIndex++] = theByte;
				theMidiState = midi_state_needs_1byte;
				break;
			case MIDI_STATUS_SYSTEM:
				if (theByte & 0x08) {// System Real Time Messages
					Endpoint_Write_Byte(theCable|CIN_SYSTEM_1BYTE);
					Endpoint_Write_Byte(theByte);
					Endpoint_Write_Byte(0);
					Endpoint_Write_Byte(0);
					Endpoint_ClearCurrentBank();
				} else if (theByte == 0xF0) {
					thePacket[thePacketIndex++] = theCable|CIN_SYSTEM_1BYTE;
					thePacket[thePacketIndex++] = theByte;
					theMidiState=midi_state_in_sysex;
				} else if (theByte == 0xF3) {//Song Select
					thePacket[thePacketIndex++] = theCable|CIN_2BYTE_SYSTEM_COMMON;
					thePacket[thePacketIndex++] = theByte;
					theMidiState=midi_state_needs_1byte;
				} else if (theByte == 0xF2) { //Song Position Pointer
					thePacket[thePacketIndex++] = theCable|CIN_3BYTE_SYSTEM_COMMON;
					thePacket[thePacketIndex++] = theByte;
					theMidiState=midi_state_needs_2bytes;
				} else if (theByte == MIDI_EOX) {
					if (thePacketIndex==3) {
						thePacket[0]=theCable|CIN_SYSEX_ENDS_IN_THREE;
					} else if (thePacketIndex==2) {
						thePacket[0]=theCable|CIN_SYSEX_ENDS_IN_TWO;
					} else {
						thePacket[0]=theCable|CIN_SYSEX_ENDS_IN_ONE;
					}
					thePacket[thePacketIndex]=0xF7;
					theMidiState=midi_state_idle;
					thePacketIndex=4;
				} else {
					// undefined -- bin it.
					theMidiState=midi_state_idle;
				}

		}
	} else {
		switch(theMidiState) {
			case midi_state_needs_2bytes:
				thePacket[thePacketIndex++]=theByte;
				theMidiState = midi_state_needs_1byte;
				break;
			case midi_state_needs_1byte:
				thePacket[thePacketIndex++]=theByte;
				thePacketIndex=4;
				theMidiState = midi_state_idle;
				break; //this was missing in the first post
			case midi_state_in_sysex:
				thePacket[thePacketIndex++]=theByte;
				break;
		}
		if(thePacketIndex==4){
			while (!(Endpoint_ReadWriteAllowed()));
			Endpoint_Write_Byte(thePacket[0]);
			Endpoint_Write_Byte(thePacket[1]);
			Endpoint_Write_Byte(thePacket[2]);
			Endpoint_Write_Byte(thePacket[3]);
			Endpoint_ClearCurrentBank();
			thePacket[0]=thePacket[1]=thePacket[2]=thePacket[3]=0;
			thePacketIndex=0;
		}

	}


}

All Hail the blinkin lights!

Thursday, November 13th, 2008

Wanna dorkboard but afraid of surface mount?

Brian Riley at Wulfden will sell you a stripped down dorkboard kit with the reset pullup resister and the programming jumper ALREADY SOLDERED! and for another 6 bucks you can build a nifty flashlight!

http://www.1strecon.org/TheShoppe/flashlight/index.shtml


Schematic for the flashlight kit.

Bootloaders? We dont need no stinking Bootloaders!

Wednesday, November 5th, 2008

Using the new features of arduino 12.

There is much that is new in the new arduino. Most of it in the user libraries and all of it good. My favorite is the addition of the ability to specify a different programmer as long as its avrdude approved.

boards.txt and programmers.txt

If you select the boards on my Arduino setup it will look very different than the stock Arduino.

In the arduino software folder you will find the boards.txt in a folder under hardware. To add a board you copy one of the existing entries and modify it to fit your board. The sample I use here is from the Sanguino, a mega 644P based arduino “core” (see www.sanguino.cc for instuctions on installing this core with arduino 12).

##############################################################

sanguino.name=Sanguino W/ bootloader

sanguino.upload.using=bootloader
sanguino.upload.protocol=stk500
sanguino.upload.maximum_size=63488
sanguino.upload.speed=38400

sanguino.bootloader.low_fuses=0xFF
sanguino.bootloader.high_fuses=0xDC
sanguino.bootloader.extended_fuses=0xFD
sanguino.bootloader.path=atmega644
sanguino.bootloader.file=ATmegaBOOT_644.hex
sanguino.bootloader.unlock_bits=0x3F
sanguino.bootloader.lock_bits=0x0F
sanguino.build.mcu=atmega644P
sanguino.build.f_cpu=16000000L
sanguino.build.core=sanguino
sanguino.verbose=false

I have a maze following mouse that I built a year and a half ago that uses the atMega644V which is very similar to the 644P, after looking at the underlying code and seeing that it is written well enough to adjust to the differences between the two chips (mostly an extra serial port), I was pretty sure that the code would compile and run on the older chip. So I modified the board entry to match my board.

##############################################################

megmouse.name=MegMouse W/ avrispmkII

megmouse.upload.using=avrispmk2
megmouse.upload.maximum_size=63488
megmouse.upload.speed=38400

megmouse.bootloader.low_fuses=0xFF
megmouse.bootloader.high_fuses=0xDC
megmouse.bootloader.extended_fuses=0xFD
megmouse.bootloader.path=atmega644
megmouse.bootloader.file=ATmegaBOOT_644.hex
megmouse.bootloader.unlock_bits=0x3F
megmouse.bootloader.lock_bits=0x0F
megmouse.build.mcu=atmega644
megmouse.build.f_cpu=8000000L
megmouse.build.core=sanguino
megmouse.verbose=false

The first chunk in each entry is the category or class of the board and groups the text entries together. I wont pretend to be a java programmer and understand how it works just that the above works.

The important changes needed were in the .build lines where I changed the mcu, the clock speed and the core (location of the core source). These are used to compile the program with avr-gcc.

The other noticable change is the .upload.using line which is new in arduino 12. (see http://arduino.cc/en/Main/ReleaseNotes)

(If you can find the the preferences.txt you will see that this is usually set to bootloader.)

The value here coresponds to an entry in programmers.txt which was added in 10 to support burning bootloaders. My entry for the mark 2 looks like this.

avrispmk2.name=avrispmk2
avrispmk2.communication=usb
avrispmk2.protocol=stk500v2

To load the new board and programmer I quit out of arduino and start it up again. Now when I look at my boards I see the new entry in the menu. Selecting this board will compile arduino’s libraries using the new target. Selecting verify will compile your code. This code will give meg deamon eyes.

/*
 * evil MegMouse eyes
 */

int leftMotorFwdPin=11;
int rightMotorFwdPin=9;
int leftMotorBackPin=8;
int rightMotorBackPin=10;
int leftMotorPwmPin=12;
int rightMotorPwmPin=13;
int rightEyePin=14;
int leftEyePin=15;

void setup()                    // run once, when the sketch starts
{
  pinMode(leftMotorFwdPin, OUTPUT);      // sets the digital pin as output
  digitalWrite(leftMotorFwdPin, LOW);      // sets the digital pin as output
  pinMode(leftMotorBackPin, OUTPUT);      // sets the digital pin as output
  digitalWrite(leftMotorBackPin, LOW);      // sets the digital pin as output
  pinMode(rightMotorFwdPin, OUTPUT);      // sets the digital pin as output
  digitalWrite(rightMotorFwdPin, LOW);      // sets the digital pin as output
  pinMode(rightMotorBackPin, OUTPUT);      // sets the digital pin as output
  digitalWrite(rightMotorBackPin, LOW);      // sets the digital pin as output
  analogWrite(leftMotorPwmPin,0);             // turn motor pwm off.
  analogWrite(rightMotorPwmPin,0);
  analogWrite(leftEyePin,0);                       // turn led pwm off.
  analogWrite(rightEyePin,0);
}

void loop()                     // run over and over again
{ int i;
  for (i = 0; i < 254; i++){
  analogWrite(leftEyePin,i);
  analogWrite(rightEyePin,i);    // fade eyes to on
  delay(2);
  }
  for (i = 254; --i >= 0; ){
  analogWrite(leftEyePin,i);   //
  analogWrite(rightEyePin,i);    // fade them to off
  delay(2);
  }
}

And the upload button will pass the parameters from your modified programmers.txt to avrdude and use the external programmer rather than requiring a bootloader and a reset.

Benito Sanguino Arduino.

Saturday, November 1st, 2008

For a while now I have wanted to check out the sanguino project (www.sanguino.cc) firsthand. I wanted to see this thing that I had tried half heartedly to work out a couple of times in the past year and a half (i.e. running the arduino code on the atmega 644) actually running. I also wanted to test the benito against a new board and I wanted to revive the “MegMouse” boards I did over a year ago to control a couple of maze solving robots.

So I went to the Wulfden FreeDuino Shop Wulfden FreeDuino Shop (www.wulfden.org/TheShoppe/freeduino/) for one of his new Sanguinos. I assembled it (more or less) in a few hours at a friends house last week but then didnt ge’t the cable made for it until yesterday.

In the mean time I downloaded arduino 12 and the new “core” provided byt the sanguino team. After adding the new section to the boards.txt like the documentation said to do I selected the new board from the tools menue. When you change boards the arduino compiles its libraries against the new processor. This is outside of the core and the new Servo library would not compile. It was an easy fix though adding the new processor to the code distinguishing the mega8 timer register names from the mega168. The next time I went back to the sanguino site a new version already addressed this issue. I suspect as the 328s become readily avaliable some of these issues will get resolved within the arduino team itself.

Finally I was able to build out the cable for the benito.

The pinouts for thos inquiring minds go like this.

I still havent gotten used to the idea that red is the right color for the debug leds (green is for power) so I put mine in wrong (the power led is also backwards so it doesnt glow) but once I changed the code led from pin 13 to pin 0 “blink” blinked.

Then I went to the first thing that I could find that required more pins to drive then the arduino had. I pulled an old led display out of the junk heap. There were 15 segments on 7 digits(22 pins).

/*
 * Bad coder no docs / copyleft here...
 */
#define NSEGMENTS 15
#define NDIGITS 7
#define BLANK 36
int ledPin = 0;
//                   a, b, c, d, e, f, g, h, j, k, l, m, n, p,dp
int SegmentPin[] = {30,31, 2, 7, 5, 1,28,25,29,27, 4, 6,26,24, 3};
int DigitPin[] = {15,16,17,18,21,20,19};
char charmap[][14]={
 //a,b,c,d,e,f,g,h,j,k,l,m,n,p
  {0,0,0,0,0,0,1,0,1,1,1,0,1,1}, //0
  {1,1,1,1,1,1,0,1,1,1,0,1,1,1}, //1
  {0,0,1,0,1,1,1,1,0,1,1,0,1,1}, //2
  {0,1,0,0,1,1,1,0,0,1,1,1,1,1}, //3
  {1,0,0,1,1,0,1,1,0,1,1,1,0,1}, //4
  {0,1,0,0,1,0,1,1,0,1,1,1,0,1}, //5
  {0,1,0,0,0,0,1,1,0,1,1,1,0,1}, //6
  {0,1,1,1,1,1,1,0,1,1,0,1,1,1}, //7
  {0,0,0,0,0,0,1,1,0,1,1,1,0,1}, //8
  {0,0,0,0,1,0,1,1,0,1,1,1,0,1}, //9
  {0,0,0,1,0,0,1,1,0,1,1,1,0,1}, //A
  {0,0,0,0,1,1,0,1,0,1,0,1,1,1}, //B
  {0,1,1,0,0,0,1,1,1,1,1,1,1,1}, //C
  {0,0,0,0,1,1,0,1,1,1,0,1,1,1}, //D
  {0,1,1,0,0,0,1,1,1,1,1,1,0,1}, //E
  {0,1,1,1,0,0,1,1,1,1,1,1,0,1}, //F
  {0,1,0,0,0,0,1,1,0,1,1,1,1,1}, //G
  {1,0,0,1,0,0,1,1,0,1,1,1,0,1}, //H
  {0,1,1,0,1,1,0,1,1,1,0,1,1,1}, //I
  {1,0,0,0,0,1,1,1,1,1,1,1,1,1}, //J
  {1,1,1,1,0,0,1,0,1,0,1,1,0,1}, //K
  {1,1,1,0,0,0,1,1,1,1,1,1,1,1}, //L
  {1,0,0,1,0,0,1,0,1,1,1,1,1,0}, //M
  {1,0,0,1,0,0,1,1,1,0,1,1,1,0}, //N
  {0,0,0,0,0,0,1,1,1,1,1,1,1,1}, //O
  {0,0,1,1,0,0,1,1,0,1,1,1,0,1}, //P
  {0,0,0,0,0,0,1,1,1,0,1,1,0,1}, //Q
  {0,0,1,1,0,0,1,1,0,0,1,1,0,1}, //R
  {0,1,0,0,1,0,1,1,0,0,1,1,0,1}, //S
  {0,1,1,1,1,1,0,1,1,1,0,1,1,1}, //T
  {1,0,0,0,0,0,1,1,1,1,1,1,1,1}, //U
  {1,1,1,1,0,0,1,0,1,1,1,0,1,1}, //V
  {1,0,0,1,0,0,1,1,1,0,1,0,1,1}, //W
  {1,1,1,1,1,1,1,0,1,0,1,0,1,0}, //X
  {1,1,1,1,1,1,1,0,1,1,0,1,1,0}, //Y
  {0,1,1,0,1,1,1,0,1,1,1,0,1,1}, //Z
  {1,1,1,1,1,1,1,1,1,1,1,1,1,1}, //
  {1,1,1,1,1,1,1,1,1,1,1,1,1,1}

};

char ascii2charmap (char ac) {
  if (ac>'z') return BLANK;
  if (ac>='a') return ((ac-'a')+10);

  if (ac>'Z') return BLANK;
  if (ac>='A') return ((ac-'A')+10);
  if (ac>'9') return BLANK;
  if (ac>='0') return (ac-'0');
}


void setup()                    // run once, when the sketch starts
{ int i;
 for (i=0;i<NSEGMENTS;i++) {
  pinMode(SegmentPin[i], OUTPUT);      // sets the digital pin as output
  digitalWrite(SegmentPin[i], HIGH);      // sets the digital pin as output
 }
 for (i=0;i<NDIGITS;i++) {
  pinMode(DigitPin[i], OUTPUT);      // sets the digital pin as output
  digitalWrite(DigitPin[i], HIGH);      // sets the digital pin as output
 }
  pinMode(ledPin, OUTPUT);      // sets the digital pin as output
}

void loop()                     // run over and over again
{ int d,i;
  char *string="Dorkbot";
  for (d=0;d<7;d++){
   for (i=0;i<14;i++) {
       digitalWrite(SegmentPin[i],charmap[ascii2charmap(string[d])][i]);
   }
   digitalWrite(DigitPin[d],HIGH);
   delay(5);
   digitalWrite(DigitPin[d],LOW);
 }

  //delay(100);                  // waits for a second

}

E Whah La.