<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>imsolidstate &#187; Electronics</title>
	<atom:link href="http://www.imsolidstate.com/archives/category/electronics/feed" rel="self" type="application/rss+xml" />
	<link>http://www.imsolidstate.com</link>
	<description>Always improving things...</description>
	<lastBuildDate>Mon, 30 Aug 2010 17:54:03 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.4</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Pressure mapping with DIY foam load cells</title>
		<link>http://www.imsolidstate.com/archives/758</link>
		<comments>http://www.imsolidstate.com/archives/758#comments</comments>
		<pubDate>Mon, 30 Aug 2010 17:54:03 +0000</pubDate>
		<dc:creator>imsolidstate</dc:creator>
				<category><![CDATA[Electronics]]></category>
		<category><![CDATA[Horses]]></category>
		<category><![CDATA[ATMega]]></category>
		<category><![CDATA[AVR]]></category>
		<category><![CDATA[Horse]]></category>
		<category><![CDATA[PCB]]></category>

		<guid isPermaLink="false">http://www.imsolidstate.com/?p=758</guid>
		<description><![CDATA[I had an idea a while back to make a pressure sensing pad for testing saddle fit on horses. The intent was to create an array of pressure sensing cells, which could then be used to produce a pressure map that would represent any pinch points on a horse&#8217;s back. I found that you can buy this [...]]]></description>
			<content:encoded><![CDATA[<p>I had an idea a while back to make a pressure sensing pad for testing saddle fit on horses. The intent was to create an array of pressure sensing cells, which could then be used to produce a pressure map that would represent any pinch points on a horse&#8217;s back. I found that you can buy this sort of thing, but it&#8217;s way too expensive for the average guy. I decided to try and make my own for cheap. I ended up making one from a handful of copper-clad PCBs and 1/4&#8243; shipping foam.</p>
<p><img class="alignnone size-large wp-image-769" title="Pressure map pad and electronics" src="http://www.imsolidstate.com/wp-content/uploads/2010/08/NewStuff-014-1024x768.jpg" alt="Pressure map pad and electronics" width="645" height="484" /></p>
<p>The active area of the pad is approximately 2&#8242; x 2&#8242;. I think the foam is polyurethane open-cell foam but I&#8217;m not sure. It&#8217;s the stuff you use to pack shipping crates. The load cells are made by sandwiching the foam in between 1&#8243; circle cutouts of copper-clad FR4 PC board. I used single sided board and a hole saw with the pilot bit removed (use a drill press and a clamp). The capacitance varies as the foam compresses, and the amount of capacitance is directly related to the thickness and density of the foam, as well as the area of the copper conductors (plates). So you can create any size or thickness load cell you want really. A bigger plate results in more capacitance, as does placing the plates closer together. I estimated the capacitance I would have in my application with <a href="http://www.daycounter.com/Calculators/Plate-Capacitor-Calculator.phtml">this calculator</a> I found at Daycounter engineering services.</p>
<p>I created an array of 64 cells by making 8 rows and 8 columns, each with 8 copper-clad discs. Wherever the row/column discs align a load cell is created. An AVR typically has eight available ADC inputs along with another eight control outputs, so this way you can scan down through the rows and columns to measure each cell. A square wave is sequentially output on the columns, and after some analog proccessing the AVR&#8217;s ADC scans each row. The analog voltage present represents the amount of pressure (capacitance) at each site.<span id="more-758"></span></p>
<p><img class="size-medium wp-image-771 alignnone" title="Pressure map electronics back" src="http://www.imsolidstate.com/wp-content/uploads/2010/08/NewStuff-006-300x225.jpg" alt="Pressure map electronics back" width="300" height="225" /><img class="size-medium wp-image-772 alignnone" title="Pressure map electronics front" src="http://www.imsolidstate.com/wp-content/uploads/2010/08/NewStuff-008-300x225.jpg" alt="Pressure map electronics front" width="300" height="225" /></p>
<p>The square wave generator is the old two op-amp ramp generator/comparator circuit. This is fed to one side of the cell. The output from the other side is coupled through whatever capacitance is available at the cell, and results in a triangle wave with amplitude proportional to the capacitance thanks to a <a href="http://electronicdesign.com/article/analog-and-mixed-signal/what-s-all-this-transimpedance-amplifier-stuff-any.aspx">transimpedance amplifier</a>. Then it is fed to a peak detector and amplified. The signal is then sent to the ADC. There are more than a few examples out on the web for capacitance sensing, <a href="http://www.discovercircuits.com/DJ-Circuits/low-value-cap-meter.htm">here is a good one</a>.</p>
<p>The AVR outputs the 64 values on a serial port. I used excel&#8217;s surface chart to represent the data. The chart posted here is a map of an english saddle.</p>
<p><img class="alignnone size-full wp-image-765" title="English saddle pressure map" src="http://www.imsolidstate.com/wp-content/uploads/2010/08/EnglishMap.JPG" alt="English saddle pressure map" width="585" height="490" /></p>
<p>It works fairly well, but my analog section needs some improvement. Sensitivity is sufficient for this test, but it&#8217;s relatively poor. Response time also suffers because of noise; my prototype could definitely use improvement in that area and I had to decrease the response time of the amplifier to get consistent output. This is unfortunate because I had planned on trying to make a movie by recording real-time data while riding a horse. I even designed it to run on a 9V battery for this purpose. Maybe if I have enough free time next summer (unlikely) I&#8217;ll try and tune it up a bit.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.imsolidstate.com/archives/758/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>SMS remote control</title>
		<link>http://www.imsolidstate.com/archives/708</link>
		<comments>http://www.imsolidstate.com/archives/708#comments</comments>
		<pubDate>Sat, 17 Jul 2010 02:42:23 +0000</pubDate>
		<dc:creator>imsolidstate</dc:creator>
				<category><![CDATA[Automation]]></category>
		<category><![CDATA[Electronics]]></category>
		<category><![CDATA[ATMega]]></category>
		<category><![CDATA[AVR]]></category>
		<category><![CDATA[C]]></category>
		<category><![CDATA[PCB]]></category>

		<guid isPermaLink="false">http://www.imsolidstate.com/?p=708</guid>
		<description><![CDATA[I&#8217;ve been expirimenting with cheap GSM cell phones as remote control devices. I wanted to be able to control stuff at my house just by sending a text from my phone. I also wanted to use an AVR since the options are pretty limitless as to what you can control. I started off simple with relay [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve been expirimenting with cheap GSM cell phones as remote control devices. I wanted to be able to control stuff at my house just by sending a text from my phone. I also wanted to use an AVR since the options are pretty limitless as to what you can control. I started off simple with relay outputs for stuff like the garage door, outside lights, etc. It could also be useful for the AVR to send texts based on events, but I haven&#8217;t messed with this yet.</p>
<p><img class="alignnone size-large wp-image-729" title="Cell phone and AVR" src="http://www.imsolidstate.com/wp-content/uploads/2010/07/Stuff-039-1024x768.jpg" alt="Cell phone and AVR" width="645" height="484" /></p>
<p>I planned on just having the AVR recognize a particular text string, like &#8220;open garage&#8221; if I wanted to let someone in my house when I&#8217;m not there without giving them a key for example. It&#8217;s not overly secure, but you could add a number sequence as a prefix to the command that would be like a password. Then you could periodically change your password if you wanted.<span id="more-708"></span></p>
<p>I&#8217;m using cell phones from Siemens. They are older phones with true serial comms. You can score one for $20 or so from ebay. The only thing that sucks about these phones is they use PDU mode for text messages. It&#8217;s a really inconvenient way to send text strings. There&#8217;s a ton of setup to each packet and each character is encoded in seven bits. I haven&#8217;t been able to get my code working with the PDU method yet. It gets stuck trying to decode the received message. I&#8217;m going to post it here to see if anyone has any suggestions.  I&#8217;m all self-taught in C. I&#8217;ve never tried comparing strings before, so that bit is a little cumbersome. I don&#8217;t like using the built-in libraries to do that kind of stuff until I figure out how it works. The only thing I think turned out nice in the code is the unpacking function.</p>
<p>Obviously, you&#8217;d be ahead of the game if you found a serial-interface phone that used the regular text mode instead of PDU. I couldn&#8217;t find any phones that did text mode and didn&#8217;t have a USB interface. (at least not for cheap) Programming a USB host interface is beyond my capabilities on the AVR.</p>
<p>References: <a href="http://www.developershome.com/sms/">Developer&#8217;s Home SMS Tutorial</a></p>
<pre>#include &lt;avr/io.h&gt;
#include &lt;avr/interrupt.h&gt;

#define unpack_state1 0
#define unpack_state2 1
#define unpack_state3 2
#define unpack_state4 3
#define unpack_state5 4
#define unpack_state6 5
#define unpack_state7 6
#define unpack_state8 7
#define F_CPU       8000000
#define BAUD   9600
#define MYUBRR  (F_CPU/(BAUD*(unsigned long)16))-1

void init_USART(void);
void init_timer0(void);
void turnoff_timer0(void);
void init_timer1(void);
void check_sms(void);
void decode_sms(void);
char unpack(unsigned char octet);
void USART_write(unsigned char *buf);

volatile unsigned char rcv_buf[128];
volatile unsigned char rcv_buf_idx;
volatile unsigned char check_message, check_online;

void main(void)
{
 DDRD |= 0x03;
 PORTD &amp;= ~0x03;
 DDRC |= 0x3F;
 PORTC &amp;= ~0x3F;
 DDRB |= 0x3F;
 PORTB &amp;= ~0x3F;
 init_USART();
 init_timer0();
 sei();

 while(check_online&lt;0xF7)
 {
  rcv_buf_idx= 0x00;
  USART_write("at");
  while((rcv_buf_idx&lt;0x08)&amp;(check_online&lt;0x30)) {}
  if( rcv_buf[0x05]==0x4F &amp;
   rcv_buf[0x06]==0x4B)
  {
   PORTB &amp;= ~(1&lt;&lt;PORTB0);   //diagnostic leds
   PORTB &amp;= ~(1&lt;&lt;PORTB5);   //diagnostic leds
   check_online= 0xFA;
  }
  else
  {
   if(rcv_buf_idx&lt;0x01) PORTB |= (1&lt;&lt;PORTB5);
   PORTB |= (1&lt;&lt;PORTB0);
   while(check_online&lt;0xF4){} //8s
   check_online= 0x00;
  }
 }
 turnoff_timer0();
 init_timer1();

while(1)
 {
  if(check_message) check_sms();
 }
}

ISR(USART_RX_vect)
{
 rcv_buf[rcv_buf_idx]= UDR0;
 rcv_buf_idx++;
}

ISR(TIMER0_OVF_vect)
{
 check_online++;
}

ISR(TIMER1_OVF_vect)
{
 check_message= 0x01;
}

void init_USART(void)
{
 UCSR0B |= (1&lt;&lt;RXCIE0)|(1&lt;&lt;RXEN0)|(1&lt;&lt;TXEN0);
 UCSR0C |= (1&lt;&lt;UCSZ00)|(1&lt;&lt;UCSZ01);

 UBRR0L = MYUBRR;
 UBRR0H = (MYUBRR &gt;&gt; 8);
}

void init_timer0(void)
{
 TCCR0B |= (1&lt;&lt;CS00)|(1&lt;&lt;CS02);  //clk/1024
 TIMSK0 |= (1&lt;&lt;TOIE0);
}

void turnoff_timer0(void)
{
 TCCR0B &amp;= ~((1&lt;&lt;CS00)|(1&lt;&lt;CS02));  //clk/1024
 TIMSK0 &amp;= ~(1&lt;&lt;TOIE0);
}

void init_timer1(void)
{
 TCCR1B |= (1&lt;&lt;CS10)|(1&lt;&lt;CS12);  //clk/1024
 TIMSK1 |= (1&lt;&lt;TOIE1);
}

void check_sms(void)
{
 rcv_buf_idx= 0x00;
 USART_write("at+cmgl");
 while(TCNT1&lt;0x90) {}
 if( rcv_buf[0x0A]==0x4F &amp;
  rcv_buf[0x0B]==0x4B) {PORTB &amp;= ~(1&lt;&lt;PORTB1); PORTB &amp;= ~(1&lt;&lt;PORTB5);}
 else if(rcv_buf[0x0A]=='+' &amp;
   rcv_buf[0x0B]=='C' &amp;
   rcv_buf[0x0C]=='M' &amp;
   rcv_buf[0x0D]=='G' &amp;
   rcv_buf[0x0E]=='L' &amp;
   rcv_buf[0x0F]==':') {PORTB &amp;= ~(1&lt;&lt;PORTB1); PORTB &amp;= ~(1&lt;&lt;PORTB5); decode_sms();}
 else
 {
  PORTB |= (1&lt;&lt;PORTB1);
  if(rcv_buf_idx&lt;0x01) PORTB |= (1&lt;&lt;PORTB5);
 }
 check_message= 0x00;
}

void decode_sms(void)
{
 unsigned char offset;
 while(rcv_buf_idx&lt;0x17){}
 unsigned char lenTPDU= (((rcv_buf[0x16] &amp;= ~0x30)*10) + (rcv_buf[0x17] &amp;= ~0x30));
 lenTPDU*= 2;
 while(rcv_buf_idx&lt;0x1B){}
 unsigned char lenSMSC= (((rcv_buf[0x1A] &amp;= ~0x30)*10) + (rcv_buf[0x1B] &amp;= ~0x30));
 lenSMSC*= 2;
 offset= 0x1B;
 while(rcv_buf_idx&lt;(lenSMSC+offset+lenTPDU)){}
 //ignore SMSC part
 //ignore first byte
 offset= 0x1E;
 unsigned char len_sender_number= ((rcv_buf[offset+lenSMSC] &amp;= ~0x30)*10);
 offset= 0x1F;
 if((rcv_buf[offset+lenSMSC]&gt;0x2F) &amp; (rcv_buf[offset+lenSMSC]&lt;0x3A))
  len_sender_number+= (rcv_buf[offset+lenSMSC] &amp;= ~0x30);
 else if(rcv_buf[offset+lenSMSC]=='A') len_sender_number+= 10;
 else if(rcv_buf[offset+lenSMSC]=='B') len_sender_number+= 11;
 else if(rcv_buf[offset+lenSMSC]=='C') len_sender_number+= 12;
 else if(rcv_buf[offset+lenSMSC]=='D') len_sender_number+= 13;
 else if(rcv_buf[offset+lenSMSC]=='E') len_sender_number+= 14;
 else if(rcv_buf[offset+lenSMSC]=='F') len_sender_number+= 15;
 if(len_sender_number%2) len_sender_number++;
 rcv_buf_idx= (0x1B+lenSMSC);
 unsigned char i= len_sender_number;
 while(i)
 {
  unsigned char *sender_number= rcv_buf[++rcv_buf_idx];
  sender_number++;
  *sender_number= rcv_buf[--rcv_buf_idx];
  sender_number++;
  rcv_buf_idx += 0x02;
  i-= 0x02;
 }
 offset= 0x35;
 unsigned char num_septets= ((rcv_buf[offset+lenSMSC+len_sender_number] &amp;= ~0x30)*10);
 offset= 0x36;
 if((rcv_buf[offset+lenSMSC+len_sender_number]&gt;0x2F) &amp; (rcv_buf[offset+lenSMSC+len_sender_number]&lt;0x3A))
  num_septets+= (rcv_buf[offset+lenSMSC+len_sender_number] &amp;= ~0x30);
 else if(rcv_buf[offset+lenSMSC+len_sender_number]=='A') num_septets+= 10;
 else if(rcv_buf[offset+lenSMSC+len_sender_number]=='B') num_septets+= 11;
 else if(rcv_buf[offset+lenSMSC+len_sender_number]=='C') num_septets+= 12;
 else if(rcv_buf[offset+lenSMSC+len_sender_number]=='D') num_septets+= 13;
 else if(rcv_buf[offset+lenSMSC+len_sender_number]=='E') num_septets+= 14;
 else if(rcv_buf[offset+lenSMSC+len_sender_number]=='F') num_septets+= 15;
 offset= 0x37;
 rcv_buf_idx= (offset+lenSMSC+len_sender_number);
 unsigned char loop= 0;
 unsigned char message[16];
 unsigned char message_idx= 0x00;
 offset= 0x1C;
 while(rcv_buf_idx&lt;=(offset+lenTPDU+lenSMSC))
 {
  unsigned char octet= 0;
  if(rcv_buf[rcv_buf_idx]=='A') octet |= 0xA0;
  else if(rcv_buf[rcv_buf_idx]=='B') octet |= 0xB0;
  else if(rcv_buf[rcv_buf_idx]=='C') octet |= 0xC0;
  else if(rcv_buf[rcv_buf_idx]=='D') octet |= 0xD0;
  else if(rcv_buf[rcv_buf_idx]=='E') octet |= 0xE0;
  else if(rcv_buf[rcv_buf_idx]=='F') octet |= 0xF0;
  else octet= ((rcv_buf[rcv_buf_idx] &amp;= ~0x30)*10);
  rcv_buf_idx++;
  if(rcv_buf[rcv_buf_idx]=='A') octet |= 0x0A;
  else if(rcv_buf[rcv_buf_idx]=='B') octet |= 0x0B;
  else if(rcv_buf[rcv_buf_idx]=='C') octet |= 0x0C;
  else if(rcv_buf[rcv_buf_idx]=='D') octet |= 0x0D;
  else if(rcv_buf[rcv_buf_idx]=='E') octet |= 0x0E;
  else if(rcv_buf[rcv_buf_idx]=='F') octet |= 0x0F;
  else octet+= (rcv_buf[rcv_buf_idx] &amp;= ~0x30);
  rcv_buf_idx++;
  message[message_idx]= unpack(octet);
  message_idx++;
  if(++loop==7)
  {
   message[message_idx]= unpack(0x00);
   message_idx++;
   loop=0;
  }
 }
 if( message[0x00]=='r' &amp;
  message[0x01]=='e' &amp;
  message[0x02]=='l' &amp;
  message[0x03]=='a' &amp;
  message[0x04]=='y' &amp;
  message[0x05]==' ' &amp;
  message[0x06]=='1' &amp;
  message[0x07]==' ' &amp;
  message[0x08]=='o' &amp;
  message[0x09]=='n') {PORTB &amp;= ~(1&lt;&lt;PORTB2); PORTC |= (1&lt;&lt;PORTC0);}
 else if(message[0x00]=='r' &amp;
  message[0x01]=='e' &amp;
  message[0x02]=='l' &amp;
  message[0x03]=='a' &amp;
  message[0x04]=='y' &amp;
  message[0x05]==' ' &amp;
  message[0x06]=='1' &amp;
  message[0x07]==' ' &amp;
  message[0x08]=='o' &amp;
  message[0x09]=='f' &amp;
  message[0x0A]=='f') {PORTB &amp;= ~(1&lt;&lt;PORTB2); PORTC &amp;= ~(1&lt;&lt;PORTC0);}
 else if(message[0x00]=='r' &amp;
  message[0x01]=='e' &amp;
  message[0x02]=='l' &amp;
  message[0x03]=='a' &amp;
  message[0x04]=='y' &amp;
  message[0x05]==' ' &amp;
  message[0x06]=='2' &amp;
  message[0x07]==' ' &amp;
  message[0x08]=='o' &amp;
  message[0x09]=='n') {PORTB &amp;= ~(1&lt;&lt;PORTB2); PORTC |= (1&lt;&lt;PORTC1);}
 else if(message[0x00]=='r' &amp;
  message[0x01]=='e' &amp;
  message[0x02]=='l' &amp;
  message[0x03]=='a' &amp;
  message[0x04]=='y' &amp;
  message[0x05]==' ' &amp;
  message[0x06]=='2' &amp;
  message[0x07]==' ' &amp;
  message[0x08]=='o' &amp;
  message[0x09]=='f' &amp;
  message[0x0A]=='f') {PORTB &amp;= ~(1&lt;&lt;PORTB2); PORTC &amp;= ~(1&lt;&lt;PORTC1);}
 else if(message[0x00]=='r' &amp;
  message[0x01]=='e' &amp;
  message[0x02]=='l' &amp;
  message[0x03]=='a' &amp;
  message[0x04]=='y' &amp;
  message[0x05]==' ' &amp;
  message[0x06]=='3' &amp;
  message[0x07]==' ' &amp;
  message[0x08]=='o' &amp;
  message[0x09]=='n') {PORTB &amp;= ~(1&lt;&lt;PORTB2); PORTC |= (1&lt;&lt;PORTC2);}
 else if(message[0x00]=='r' &amp;
  message[0x01]=='e' &amp;
  message[0x02]=='l' &amp;
  message[0x03]=='a' &amp;
  message[0x04]=='y' &amp;
  message[0x05]==' ' &amp;
  message[0x06]=='3' &amp;
  message[0x07]==' ' &amp;
  message[0x08]=='o' &amp;
  message[0x09]=='f' &amp;
  message[0x0A]=='f') {PORTB &amp;= ~(1&lt;&lt;PORTB2); PORTC &amp;= ~(1&lt;&lt;PORTC2);}
 else if(message[0x00]=='r' &amp;
  message[0x01]=='e' &amp;
  message[0x02]=='l' &amp;
  message[0x03]=='a' &amp;
  message[0x04]=='y' &amp;
  message[0x05]==' ' &amp;
  message[0x06]=='4' &amp;
  message[0x07]==' ' &amp;
  message[0x08]=='o' &amp;
  message[0x09]=='n') {PORTB &amp;= ~(1&lt;&lt;PORTB2); PORTC |= (1&lt;&lt;PORTC3);}
 else if(message[0x00]=='r' &amp;
  message[0x01]=='e' &amp;
  message[0x02]=='l' &amp;
  message[0x03]=='a' &amp;
  message[0x04]=='y' &amp;
  message[0x05]==' ' &amp;
  message[0x06]=='4' &amp;
  message[0x07]==' ' &amp;
  message[0x08]=='o' &amp;
  message[0x09]=='f' &amp;
  message[0x0A]=='f') {PORTB &amp;= ~(1&lt;&lt;PORTB2); PORTC &amp;= ~(1&lt;&lt;PORTC3);}
 else if(message[0x00]=='r' &amp;
  message[0x01]=='e' &amp;
  message[0x02]=='l' &amp;
  message[0x03]=='a' &amp;
  message[0x04]=='y' &amp;
  message[0x05]==' ' &amp;
  message[0x06]=='5' &amp;
  message[0x07]==' ' &amp;
  message[0x08]=='o' &amp;
  message[0x09]=='n') {PORTB &amp;= ~(1&lt;&lt;PORTB2); PORTC |= (1&lt;&lt;PORTC4);}
 else if(message[0x00]=='r' &amp;
  message[0x01]=='e' &amp;
  message[0x02]=='l' &amp;
  message[0x03]=='a' &amp;
  message[0x04]=='y' &amp;
  message[0x05]==' ' &amp;
  message[0x06]=='5' &amp;
  message[0x07]==' ' &amp;
  message[0x08]=='o' &amp;
  message[0x09]=='f' &amp;
  message[0x0A]=='f') {PORTB &amp;= ~(1&lt;&lt;PORTB2); PORTC &amp;= ~(1&lt;&lt;PORTC4);}
 else if(message[0x00]=='r' &amp;
  message[0x01]=='e' &amp;
  message[0x02]=='l' &amp;
  message[0x03]=='a' &amp;
  message[0x04]=='y' &amp;
  message[0x05]==' ' &amp;
  message[0x06]=='6' &amp;
  message[0x07]==' ' &amp;
  message[0x08]=='o' &amp;
  message[0x09]=='n') {PORTB &amp;= ~(1&lt;&lt;PORTB2); PORTC |= (1&lt;&lt;PORTC5);}
 else if(message[0x00]=='r' &amp;
  message[0x01]=='e' &amp;
  message[0x02]=='l' &amp;
  message[0x03]=='a' &amp;
  message[0x04]=='y' &amp;
  message[0x05]==' ' &amp;
  message[0x06]=='6' &amp;
  message[0x07]==' ' &amp;
  message[0x08]=='o' &amp;
  message[0x09]=='f' &amp;
  message[0x0A]=='f') {PORTB &amp;= ~(1&lt;&lt;PORTB2); PORTC &amp;= ~(1&lt;&lt;PORTC5);}
 else PORTB |= (1&lt;&lt;PORTB2);
}

char unpack(unsigned char octet)
{
 static unsigned char unpack_state, remain_val;
 unsigned char septet, masked_val;

 switch(unpack_state)
 {
  case unpack_state1:
   septet= (octet &amp; 0x7F);
   remain_val= (octet &amp; 0x80);
   unpack_state= unpack_state2;
   break;
  case unpack_state2:
   masked_val= (octet &amp; 0x3F);
   septet= (masked_val&lt;&lt;1) | (remain_val&gt;&gt;7);
   remain_val= (octet &amp; 0xC0);
   unpack_state= unpack_state3;
   break;
  case unpack_state3:
   masked_val= (octet &amp; 0x1F);
   septet= (masked_val&lt;&lt;2) | (remain_val&gt;&gt;6);
   remain_val= (octet &amp; 0xE0);
   unpack_state= unpack_state4;
   break;
  case unpack_state4:
   masked_val= (octet &amp; 0x0F);
   septet= (masked_val&lt;&lt;3) | (remain_val&gt;&gt;5);
   remain_val= (octet &amp; 0xF0);
   unpack_state= unpack_state5;
   break;
  case unpack_state5:
   masked_val= (octet &amp; 0x07);
   septet= (masked_val&lt;&lt;4) | (remain_val&gt;&gt;4);
   remain_val= (octet &amp; 0xF8);
   unpack_state= unpack_state6;
   break;
  case unpack_state6:
   masked_val= (octet &amp; 0x03);
   septet= (masked_val&lt;&lt;5) | (remain_val&gt;&gt;3);
   remain_val= (octet &amp; 0xFC);
   unpack_state= unpack_state7;
   break;
  case unpack_state7:
   masked_val= (octet &amp; 0x01);
   septet= (masked_val&lt;&lt;6) | (remain_val&gt;&gt;2);
   remain_val= (octet &amp; 0xFE);
   unpack_state= unpack_state8;
   break;
  case unpack_state8:
   septet= (remain_val&gt;&gt;1);
   unpack_state= unpack_state1;
   break;
 }
 return septet;
}

void USART_write(unsigned char *buf)
{
 while(*buf)
    {
       while ( !( UCSR0A &amp; (1&lt;&lt;UDRE0)) ){}
       UDR0= *buf;
       buf++;
    }
 while ( !( UCSR0A &amp; (1&lt;&lt;UDRE0)) ){}
 UDR0= 0x0D;
}</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.imsolidstate.com/archives/708/feed</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Flatbed scanner panoramic camera</title>
		<link>http://www.imsolidstate.com/archives/712</link>
		<comments>http://www.imsolidstate.com/archives/712#comments</comments>
		<pubDate>Sat, 17 Jul 2010 02:25:42 +0000</pubDate>
		<dc:creator>imsolidstate</dc:creator>
				<category><![CDATA[Automation]]></category>
		<category><![CDATA[Electronics]]></category>
		<category><![CDATA[Photography]]></category>

		<guid isPermaLink="false">http://www.imsolidstate.com/?p=712</guid>
		<description><![CDATA[I&#8217;ve always loved panoramic pictures, especially when they&#8217;re printed up big. I&#8217;m not new to panoramics, as I&#8217;ve done quite a few stitched sequences, as well as true panoramic film photography. A while back I was wondering if I could repurpose the scanning head part of a scanner into a rotating head panoramic camera. After [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve always loved panoramic pictures, especially when they&#8217;re printed up big. I&#8217;m not new to panoramics, as I&#8217;ve done quite a few <a href="http://www.imsolidstate.com/about/my-photography">stitched sequences</a>, as well as <a href="http://www.imsolidstate.com/archives/85">true panoramic film photography</a>. A while back I was wondering if I could repurpose the scanning head part of a scanner into a rotating head panoramic camera. After an initial trial with mediocre results, I did some digging and found out that people have made these before. There&#8217;s quite a few issues though: fitting a new lens, an IR filter, the proper speed rotating part, etc. An added difficulty with flatbed scanners is the scanning head scans over a white strip before every scan to calibrate the sensor, so if you don&#8217;t have something white for the scan head to look at right at the beginning of the scan, you get some really goofy color stripes. Sheet feed scanners aren&#8217;t supposed to do this, so I tried one of those but quickly tired of fooling all of the little switches. It always thinks there&#8217;s a paper jam.</p>
<p><img class="alignnone size-large wp-image-727" title="Line scanner" src="http://www.imsolidstate.com/wp-content/uploads/2010/07/Stuff-037-1024x768.jpg" alt="Line scanner" width="645" height="484" /></p>
<p>The scan head sensor is actually pretty cool, and by definition it&#8217;s a line scan camera. It would still be cool to set it up as a line scan camera to play with.</p>
<p>After seeing <a href="http://hackaday.com/2010/07/14/panoramic-and-spheric-tripod-rig/">this rig </a>at HAD, I&#8217;m convinced I&#8217;m wasting my time. Moving forward I will be designing a stepper-driven tripod mount for my camera and use stitching software instead. If anybody has any suggestions on good software to try let me know. I&#8217;ve been using Panorama Maker or whatever the Nikon bundled program was called. It just doesn&#8217;t always do a very good job, even with the special mount I made that&#8217;s supposed to <a href="http://reallyrightstuff.com/pano/03.html">eliminate parallax</a>. I get a lot of blurring at the upper and lower edge stitches. I would also like to experiment with multi-row so I can use longer lenses.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.imsolidstate.com/archives/712/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Build a 9-digit Pulse Counter for under $20</title>
		<link>http://www.imsolidstate.com/archives/665</link>
		<comments>http://www.imsolidstate.com/archives/665#comments</comments>
		<pubDate>Thu, 29 Apr 2010 21:30:56 +0000</pubDate>
		<dc:creator>imsolidstate</dc:creator>
				<category><![CDATA[Electronics]]></category>

		<guid isPermaLink="false">http://www.imsolidstate.com/?p=665</guid>
		<description><![CDATA[Recently I was fixing a piece of equipment and wanted a pulse counter to verify a stepper motor system. The existing linear slide was failing, but had a non-standard TPI. The step output was already programmed into a PLC. I had a programmable motor drive, but needed to know how many steps were commanded for [...]]]></description>
			<content:encoded><![CDATA[<p>Recently I was fixing a piece of equipment and wanted a pulse counter to verify a stepper motor system. The existing linear slide was failing, but had a non-standard TPI. The step output was already programmed into a PLC. I had a programmable motor drive, but needed to know how many steps were commanded for a given linear move so I could electronically gear the system to a new linear slide.</p>
<p><img class="alignnone size-large wp-image-674" title="Pulse Counter" src="http://www.imsolidstate.com/wp-content/uploads/2010/04/PulseCounter-0021-1024x525.jpg" alt="PulseCounter 002" width="645" height="331" /></p>
<p>That&#8217;s when I found the MC14453 3-digit BCD counter from ON semiconductor. ON semi has integrated three decade counters with an oscillator that multiplexes the 3 digits of an LED display. You pair it with an MC14543 7-segment decoder to have a three segment counter. You can cascade as many of these two chips as necessary by using the overflow output to clock the next stage, and feeding the first scan clock to the next multiplexer. I made a 9-digit counter, but you can do any multiple of three.</p>
<p>I used a MAX232 to drive the input of the counter. It&#8217;s a bit of a waste of the MAX232&#8217;s abilities, but the MAX232 does a great job of handling a wide range of input signals and it&#8217;s only about a buck.  The chip can handle inputs from +30V to -30V, and still triggers between .5 and 1.5V with a 5V digital output. It also offers +/-15kV ESD protection on the inputs. With this capability you can sample a variety of different signals without any level shifting (even AC). With a few more input components (current limit resistor, zener clamp, series capacitor) you could capacitively couple the signal and measure anything. I wanted good low frequency performance so I skipped that part.</p>
<p>The parts cost about $6 per 3-digit stage. They are all available at <a href="http://www.digikey.com">Digi-Key</a>. Part numbers:<br />
MC14453: MC14553BCPGOS-ND<br />
MC14543: MC14543BCPGOS-ND<br />
MAX232: 296-6940-5-ND<br />
7-Seg display: 160-1544-5-ND</p>
<p>The only problem with this arrangement is that the MC14543 is driving the LED segments, so it&#8217;s limited to less than 10mA drive current. That makes the segments a bit dimmer than I&#8217;d like for easy viewing. If I was doing it again I&#8217;d get some cheap transistors to run more current. The display is capable of 20mA.</p>
<p>The schematic can be found in the datasheet for the <a href="http://www.onsemi.com/pub_link/Collateral/MC14553B-D.PDF">MC14553</a>. Keep reading for the layout and schematic for my build.</p>
<p><span id="more-665"></span></p>
<p>Click images for full-size.  I mixed up the bias for the hold and reset inputs, if you use momentary off-on switches replace reset with R25 and hold with R26. Otherwise make sure the switches are normally closed.</p>
<p><a href="http://www.imsolidstate.com/wp-content/uploads/2010/04/PulseCounterLayout.png"><img class="alignnone size-large wp-image-700" title="Pulse Counter PCB Layout" src="http://www.imsolidstate.com/wp-content/uploads/2010/04/PulseCounterLayout-1024x739.png" alt="PulseCounterLayout" width="645" height="465" /></a></p>
<p><a href="http://www.imsolidstate.com/wp-content/uploads/2010/04/PulseCounterSchematic.png"><img class="alignnone size-large wp-image-701" title="PulseCounterSchematic" src="http://www.imsolidstate.com/wp-content/uploads/2010/04/PulseCounterSchematic-1024x576.png" alt="PulseCounterSchematic" width="645" height="363" /></a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.imsolidstate.com/archives/665/feed</wfw:commentRss>
		<slash:comments>14</slash:comments>
		</item>
		<item>
		<title>Trailer turn signal update</title>
		<link>http://www.imsolidstate.com/archives/680</link>
		<comments>http://www.imsolidstate.com/archives/680#comments</comments>
		<pubDate>Thu, 29 Apr 2010 21:29:43 +0000</pubDate>
		<dc:creator>imsolidstate</dc:creator>
				<category><![CDATA[6.0L Ford Super Duty]]></category>
		<category><![CDATA[Electronics]]></category>
		<category><![CDATA[Trailer]]></category>
		<category><![CDATA[Truck]]></category>

		<guid isPermaLink="false">http://www.imsolidstate.com/?p=680</guid>
		<description><![CDATA[If anyone was interested in my trailer turn signal project, (there was a lot of haters on that one, esp. on Hackaday) Pacman left a comment on that post about an RV turn signal adapter for towing a car at etrailer.com. I bought one of these to try it out. Turns out it does seperate [...]]]></description>
			<content:encoded><![CDATA[<p>If anyone was interested in my <a href="http://www.imsolidstate.com/archives/542">trailer turn signal</a> project, (there was a lot of haters on that one, esp. on <a href="http://hackaday.com/2009/12/31/trailer-side-indicator-lights/">Hackaday</a>) Pacman left a comment on that post about an RV turn signal adapter for towing a car at <a href="http://www.etrailer.com/p-118158.html">etrailer.com</a>. I bought one of these to try it out. Turns out it does seperate the brake signal from the left and right turn composite signal. So it would work for what I was trying to do. The only thing it doesn&#8217;t do that mine did was let the light also act as a clearance light. With my design, the light would be on if the clearance lights were on and flash off to indicate a signal. Not really a big deal though, because you can buy clearance/signal lights to replace the existing clearance lights and use both signals with the same light.</p>
<p>I wasn&#8217;t expecting much, but out of curiosity I milled off the top of the plastic box. You can see two relays sticking out of the epoxy potting.</p>
<p><img class="alignnone size-large wp-image-683" title="Turn signal/brake splitter" src="http://www.imsolidstate.com/wp-content/uploads/2010/04/Trailer-1024x768.jpg" alt="Trailer" width="645" height="484" /></p>
]]></content:encoded>
			<wfw:commentRss>http://www.imsolidstate.com/archives/680/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PCB layouts and schematics for my CNC</title>
		<link>http://www.imsolidstate.com/archives/636</link>
		<comments>http://www.imsolidstate.com/archives/636#comments</comments>
		<pubDate>Thu, 25 Mar 2010 19:59:57 +0000</pubDate>
		<dc:creator>imsolidstate</dc:creator>
				<category><![CDATA[CNC]]></category>
		<category><![CDATA[Electronics]]></category>
		<category><![CDATA[PCB]]></category>
		<category><![CDATA[Stepper]]></category>

		<guid isPermaLink="false">http://www.imsolidstate.com/?p=636</guid>
		<description><![CDATA[I&#8217;ve received some email and comment requests for the layouts and schematics of the electronics running my CNC machine. I&#8217;ve pulled together what I can find for this article.
Power Filter Boards

I made these power filters for a few reasons. One is to protect the stepper motor drives from the inductive spikes that can come off [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve received some email and comment requests for the layouts and schematics of the electronics running my CNC machine. I&#8217;ve pulled together what I can find for this article.</p>
<p><strong>Power Filter Boards</strong></p>
<p><strong><img class="alignnone size-large wp-image-643" title="Electronics 002" src="http://www.imsolidstate.com/wp-content/uploads/2010/03/Electronics-002-768x1024.jpg" alt="Electronics 002" width="538" height="717" /></strong></p>
<p>I made these power filters for a few reasons. One is to protect the stepper motor drives from the inductive spikes that can come off of the motors.  They can be pretty big. Another reason was that I wanted to use a switch-mode power supply to run the drives. There&#8217;s a lot of good reasons for this:</p>
<p>1. Switch-mode power supplies are cheaper than linear power supplies.</p>
<p>2. Switch-mode power supplies are smaller than linear supplies for equivalent output power.</p>
<p>3. Switch-mode power supplies come in a wide range of voltages, so you can run the stepper motors at the highest voltage possible. (The highest voltage your controller supports) This allows for more torque from any motor because a higher voltage will push more current through the inductance of the motor than a lower voltage. It will also do it quicker, so you should get a little more speed too.</p>
<p>However, I wasn&#8217;t sure that the chopper-style stepper driver would be okay with a switch mode supply, as it pretty much shorts out the supply every time the chopper turns on. So I needed a buffer.</p>
<p>I found <a href="http://www.eetasia.com/ART_8800475657_480600_NP_f0ccb955.HTM">this article</a> at EETimesAsia by John Betten from TI. I modified the circuit for the voltage levels I wanted to run, and also found a suitable replacement for the FET since I couldn&#8217;t find one at the time. Here is the original schematic:</p>
<p><img class="alignnone size-full wp-image-639" title="PowerFilterSchem" src="http://www.imsolidstate.com/wp-content/uploads/2010/03/PowerFilterSchem.JPG" alt="PowerFilterSchem" width="664" height="400" /></p>
<p>I&#8217;m using an IR IRFP9140N in place of Q1. I also replaced D2 with a 56V TVS from ON semi, 1.5KE56A. I used 56V because the LMD18245 motor driver IC I have has a continuous rating of 55V and an absolute max of 60V. I also oversized the output capacitor just to be on the safe side since I had some big ones laying around anyway. They are 22000uF 100V Panasonics. They are overkill, the voltage is rock-solid even when the motors are running at full clip. I wanted to be able to recycle the boards though if I ever upgrade to a bigger machine and have bigger motors. Here&#8217;s the layout for my circuit:</p>
<p><img class="alignnone size-full wp-image-641" title="FilterLayout" src="http://www.imsolidstate.com/wp-content/uploads/2010/03/FilterLayout.jpg" alt="FilterLayout" width="587" height="588" /></p>
<p><strong>Opto-isolated Parallel Interface Board</strong><strong> </strong></p>
<p>I designed this parallel interface board after killing a parallel port with a breakout board that I bought off the internet. I think it just pulled too much current from the port. I designed this board to pull the smallest amount of current from the parallel port as possible, while also providing good drive characteristics for outputs. This board is customized to my application, so the voltages and bias might not be appropriate for all.  Check to make sure your inputs will work before using the values here. I couldn&#8217;t find the schematic, just the layout but it&#8217;s not too complicated to figure out if you have the datasheets for the TLP2631 and the SN74LS244N.<span id="more-636"></span></p>
<p>The 74244 is just a buffer; high impedance inputs and good drive characteristics. All inputs and outputs have RC filters. Adjust these values to suit your application. The 2631 is an opto-isolator that keeps the PC&#8217;s voltage potentials isolated from the CNC machine&#8217;s voltage. That way no voltage spikes hit the PC and there are no ground loops with the power supplies. Complete isolation! You just have to run power over from a spare drive connector on the PC to wherever you put this board.</p>
<p><img class="alignnone size-large wp-image-646" title="Electronics 001" src="http://www.imsolidstate.com/wp-content/uploads/2010/03/Electronics-001-1024x768.jpg" alt="Electronics 001" width="645" height="484" /></p>
<p>The astute observer will notice a fried resistor on the board. That&#8217;s the board doing just what it was designed to do, protect my computer from dumb screw-ups. I accidentally miswired one of my limit switches during the build and the little resistor took a hike. I could have just replaced the resistor, but I&#8217;m not using all of the inputs so I just moved the wire over and remapped the limit input pin. Here&#8217;s the layout. Click to view full size.</p>
<p><a href="http://www.imsolidstate.com/wp-content/uploads/2010/03/Optocoupler.jpg"><img class="alignnone size-full wp-image-648" title="Optocoupler" src="http://www.imsolidstate.com/wp-content/uploads/2010/03/Optocoupler.jpg" alt="Optocoupler" width="603" height="366" /></a></p>
<p><strong>Stepper Motor Controller</strong></p>
<p><strong><img class="alignnone size-large wp-image-650" title="microstep" src="http://www.imsolidstate.com/wp-content/uploads/2010/03/microstep-1024x692.jpg" alt="microstep" width="645" height="436" /> </strong></p>
<p>I used the &#8220;Microstep&#8221; from <a href="http://www.embeddedtronics.com/">EAS Electronics</a>. It&#8217;s worked well, but the switching frequency is low enough to create an obnoxious whine when the motors aren&#8217;t moving. I read in the LMD18245 datasheet that you can adjust the chopping frequency, but I haven&#8217;t bothered to try. It&#8217; s loud enough anyway when my router &#8220;spindle&#8221; is turning that it&#8217;s not too important to me.</p>
<p>It&#8217;s PIC based and the source is available if you want to modify it to do custom stuff. The microstep ranges are 10,8,4,half, and full step.</p>
<p>My next machine will use <a href="http://www.automationdirect.com/adc/Home/Home">Automation Direct&#8217;s</a> stepper drivers. They aren&#8217;t as cheap as building your own, but they are pretty close. I used two of their <a href="http://www.automationdirect.com/adc/Shopping/Catalog/Motion_Control/Stepper_Systems/Drives_-z-_Power/STP-DRV-4035">STP-DRV-4035</a> drives in a project at work along with their stepper motors and was pleased with the results. If you need really fine step rate control, they have one that is programmable but it costs more.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.imsolidstate.com/archives/636/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Build a spot welder from a battery charger</title>
		<link>http://www.imsolidstate.com/archives/590</link>
		<comments>http://www.imsolidstate.com/archives/590#comments</comments>
		<pubDate>Thu, 11 Mar 2010 04:53:39 +0000</pubDate>
		<dc:creator>imsolidstate</dc:creator>
				<category><![CDATA[Electronics]]></category>
		<category><![CDATA[ATMega]]></category>
		<category><![CDATA[AVR]]></category>
		<category><![CDATA[C]]></category>
		<category><![CDATA[Current sense]]></category>
		<category><![CDATA[LCD]]></category>
		<category><![CDATA[PCB]]></category>

		<guid isPermaLink="false">http://www.imsolidstate.com/?p=590</guid>
		<description><![CDATA[I ran across a battery charger a while ago that was collecting dust. I looked inside and saw the transformer, heatsinks, high current bridge rectifier and SCR and knew I could do something with it. So I turned it into a spot welder.

I originally intended this project to weld thin sheetmetal tabs to stuff to [...]]]></description>
			<content:encoded><![CDATA[<p>I ran across a battery charger a while ago that was collecting dust. I looked inside and saw the transformer, heatsinks, high current bridge rectifier and SCR and knew I could do something with it. So I turned it into a spot welder.</p>
<p><img class="size-large wp-image-598 alignnone" title="Spot Welder" src="http://www.imsolidstate.com/wp-content/uploads/2010/03/SpotWelder-028-1024x768.jpg" alt="SpotWelder 028" width="645" height="484" /></p>
<p>I originally intended this project to weld thin sheetmetal tabs to stuff to act as solder tabs. The project has not been as easy as I originally thought though. (It also suffered some scope creep) It&#8217;s my first crack at 5V logic mixed with line AC voltage, and for rolling my own power supply. I used a step-down transformer, bridge regulator and a capacitor to feed an LDO regulator for the control circuit. With the low current draw of the controller, the voltage input to the regulator was relatively free from any ripple thanks to the capacitor.</p>
<p>I ended up frying a processor, LCD, and a couple other components due to a dumb move while troubleshooting the circuit, and overlooking a capacitor&#8217;s voltage rating. 120VAC will eat 5V stuff for lunch.</p>
<p><img class="alignnone size-large wp-image-599" title="Spot Welder guts" src="http://www.imsolidstate.com/wp-content/uploads/2010/03/SpotWelder-030-1024x768.jpg" alt="Spot Welder guts" width="645" height="484" /></p>
<p>The control circuit basically modulates the SCR, which is hooked up to the output of the bridge rectifier after a step-down transformer. The controller allows for adjustment of duration of the weld and amount of the rectified AC phase that is delivered to the workpiece. The controller holds off the SCR until a pre-determined time of each half phase to control power delivery. An analog comparator detects the zero point of the phase for timing purposes, via a seperate bridge rectifier that has it&#8217;s ouput fed through a large resistor to the comparator. A zener clamps the current-limited voltage at 4.8V so as not to damage the micro&#8217;s input. A high-to-low transition on the comparator triggers the zero crossing timer. The threshhold voltage is adjustable by an on-board pot.</p>
<p>I also added an Allegro hall effect current sensor that I had lying around from my <a href="http://www.imsolidstate.com/archives/9">alternator current sense project</a>. It&#8217;s overkill, but it measures the amount of peak current being delivered and displays it on the LCD.</p>
<p><img class="size-medium wp-image-603 alignleft" title="SpotWelder 034" src="http://www.imsolidstate.com/wp-content/uploads/2010/03/SpotWelder-0341-300x225.jpg" alt="SpotWelder 034" width="300" height="225" /></p>
<p>The controller is an ATmega88PA running at 8Mhz. Firmware is written in C with AVRStudio and AVR-GCC. The micro reads the power and duration settings, displays that on the LCD, along with the max current for the last weld cycle and the temperature of the mega&#8217;s on-chip sensor. The controller also handles timing duties, zero crossing detection, and control of the SCR gate. The gate is fired by a P-channel MOSFET, with the FET&#8217;s gate driven by an NPN BJT on one of the micro&#8217;s pins. A footswitch is used as input to the micro to trigger a weld cycle. Both the footswitch input and the zero crossings are buffered by a simple three-sample debouncing routine to prevent erroneous triggers. The system also checks for the footswitch input on power up and after the weld cycle is complete, and waits if the footswitch is down with a message on the LCD to release the footswitch. This allows for safety as well as eliminating any unintended re-triggers at very short durations. Duration is adjustable from roughly one ac cycle to 60 cycles (1 sec). Power control allows from 5% to 95% of each half phase to be delivered to the workpiece.</p>
<p>The SCR&#8217;s cathode voltage is available at PORTC2 as a 10:1 voltage divider, and clamped with a zener to prevent damage to the micro. I didn&#8217;t need it, so it&#8217;s not used in the code.</p>
<p>I&#8217;ve also added a power resistor to the output to limit current. I used carbon-carbon as a power resistor (I work in a carbon plant) since it&#8217;s free and power resistors are expensive. You only need a few tenths of an ohm to limit the current to a level that won&#8217;t destroy the diodes and SCR. I&#8217;m overdriving mine at about 130A maximum. It seems to handle it fine for the short bursts.  [Edit: 130A isn't enough though. I may rewire so the diodes/SCR are on the input side and push the current higher by removing or modifying the resistor. Pressure of the electrodes on the joint is also important, still figuring that out.]</p>
<p>Here&#8217;s some drive waveforms: yellow is the output voltage (it&#8217;s at 50V/div so it looks small), purple is the output current measured by the hall sensor, blue is the FET&#8217;s gate that turns on the SCR, and green is the bridge voltage.</p>
<p><img class="alignnone size-medium wp-image-604" title="Low drive" src="http://www.imsolidstate.com/wp-content/uploads/2010/03/SpotWelder-031-300x225.jpg" alt="Low drive" width="300" height="225" /><img class="alignnone size-medium wp-image-605" title="Medium drive" src="http://www.imsolidstate.com/wp-content/uploads/2010/03/SpotWelder-032-300x225.jpg" alt="Medium drive" width="300" height="225" /></p>
<p>This project has got me thinking about modifying my old &#8221;buzzbox&#8221; AC welder. I&#8217;ve got some big capacitors and IGBTs from a couple old motor drives that could give me a really nice TIG welding power supply. I think I&#8217;ve read you can weld high frequency (1-2kHz?) square-wave without needing any HF section. If I remember right square-wave with a positive DC offset is sort of the ultimate TIG welder. Anybody with comments or information about that feel free to drop me a line.</p>
<p>Continue reading for the schematic, PCB layout, and code.</p>
<p>References: <a href="http://www.millerwelds.com/pdf/Resistance.pdf">Miller Resistance Spot Welding</a></p>
<p><span id="more-590"></span></p>
<p>The schematic: (Click on the picture for full size)</p>
<p><a href="http://www.imsolidstate.com/wp-content/uploads/2010/03/SpotWelderSchem3.png"><img class="alignnone size-large wp-image-659" title="SpotWelder Schematic" src="http://www.imsolidstate.com/wp-content/uploads/2010/03/SpotWelderSchem3-1024x528.png" alt="SpotWelder Schematic" width="645" height="333" /></a></p>
<p>The board: (Click for full-size image)</p>
<p><a href="http://www.imsolidstate.com/wp-content/uploads/2010/03/SpotWelderLayout3.png"><img class="alignnone size-large wp-image-661" title="SpotWelder Layout" src="http://www.imsolidstate.com/wp-content/uploads/2010/03/SpotWelderLayout3-1024x822.png" alt="SpotWelder Layout" width="645" height="518" /></a></p>
<p>And the code. I think it&#8217;s all correct but I had to rewire a couple things on mine so you might do well to doublecheck the I/O is all correct.</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;</p>
<p>#include &lt;avr/io.h&gt;<br />
#include &lt;util/delay.h&gt;<br />
#include &lt;avr/interrupt.h&gt;<br />
//#include &lt;avr/wdt.h&gt; // can&#8217;t get the watchdog to work yet</p>
<p>#define F_CPU 8000000; //8MHz</p>
<p>//function declarations<br />
void lcd_write_byte(unsigned char CONTROL, unsigned char DATA);<br />
void initLCD(void);<br />
void updateLCD(void);<br />
void switch_up(void);<br />
void weld_message(void);</p>
<p>//global variables<br />
volatile unsigned int power, duration, temperature;<br />
volatile unsigned char current, max_current;</p>
<p>void main(void)<br />
{<br />
 unsigned int old_power, old_duration= 0; //variables for comparison</p>
<p> PRR &amp;= ~(1&lt;&lt;PRADC);  //disable ADC power reduction<br />
 ADMUX |= (1&lt;&lt;REFS0); //setup VCC as reference<br />
 ADMUX |= (1&lt;&lt;ADLAR); //left adjust result for 8 bit ADC<br />
 ADCSRA |= (1&lt;&lt;ADPS0)|(1&lt;&lt;ADPS1)|(1&lt;&lt;ADPS2); //prescaler clk/64 125kHz @ 8MHz clock<br />
 ADCSRA |= (1&lt;&lt;ADEN); //enable ADC</p>
<p> TCCR1B |= (1&lt;&lt;CS10); //enable timer1, no prescale (clk/1)</p>
<p> ACSR |= (1&lt;&lt;ACIS1); //enable analog comparator falling edge interrupt<br />
 ACSR |= (1&lt;&lt;ACIE); //enable analog comparator interrupt</p>
<p> DDRC |= (1&lt;&lt;PORTC3); //SCR gate drive<br />
 DDRC &amp;= ~((1&lt;&lt;PORTC0)|(1&lt;&lt;PORTC1)|(1&lt;&lt;PORTC2)); //set as input<br />
 PORTC &amp;= ~((1&lt;&lt;PORTC0)|(1&lt;&lt;PORTC1)|(1&lt;&lt;PORTC2)); //PUD</p>
<p> DDRD |= (1&lt;&lt;PORTD0)|(1&lt;&lt;PORTD1)|(1&lt;&lt;PORTD2)|(1&lt;&lt;PORTD3)|(1&lt;&lt;PORTD4)|(1&lt;&lt;PORTD5);<br />
 DDRD &amp;= ~((1&lt;&lt;PORTD6)|(1&lt;&lt;PORTD7)); //set as input<br />
 PORTD &amp;= ~((1&lt;&lt;PORTD6)|(1&lt;&lt;PORTD7)); //PUD</p>
<p> DDRB &amp;= ~(1&lt;&lt;PORTB0);  //footswitch input<br />
 PORTB &amp;= ~(1&lt;&lt;PORTB0);  //PUD</p>
<p> initLCD(); //set up LCD</p>
<p> switch_up(); //check footswitch</p>
<p> max_current= 0&#215;7F;<br />
 updateLCD(); //display settings</p>
<p>// wdt_reset(); //reset the watchdog timer<br />
// WDTCSR |= (1&lt;&lt;WDCE)|(1&lt;&lt;WDE); //clear the system reset/ change enable bits<br />
// WDTCSR |= (1&lt;&lt;WDE)|(1&lt;&lt;WDP0)|(1&lt;&lt;WDP1)|(1&lt;&lt;WDP2); //set new prescaler, 2 seconds</p>
<p> <br />
 while(1)<br />
 {<br />
  old_power= power;  //set up the comparsison value before getting new value<br />
  ADMUX &amp;= ~((1&lt;&lt;MUX0)|(1&lt;&lt;MUX1)|(1&lt;&lt;MUX2)|(1&lt;&lt;MUX3)); //sample ADC0 (power setting)<br />
  ADCSRA |= (1&lt;&lt;ADSC); //start conversion<br />
  while(ADCSRA &amp; (1&lt;&lt;ADSC)){} //wait for conversion<br />
  power= ADCH;<br />
  power*= 234; //scale power for use later: ((2^8)*234)= 59904 max value<br />
      //59904 clk cycles= (1/8000000)*59904= 7.5ms<br />
      //7.5ms= (.0075/(1/120))*100= 90% of one half wave (60Hz)<br />
      //with these values power can be up to 90% of each half wave,<br />
      //which with the 5% coded into the ISR, yields a range of 5%-95%.</p>
<p>  old_duration= duration;  //set up the comparsison value before getting new value<br />
  ADMUX |= (1&lt;&lt;MUX0);  //sample ADC1 (duration setting)<br />
  ADCSRA |= (1&lt;&lt;ADSC); //start conversion<br />
  while(ADCSRA &amp; (1&lt;&lt;ADSC)){} //wait for conversion<br />
  duration= ADCH; <br />
  duration= (duration&gt;&gt;1); //convert to 7-bit number, limits duration to ~1 second<br />
  if(duration&lt;=0&#215;0007) duration= 0&#215;0000;<br />
   else duration-= 0&#215;0007; //subtract 7 so duration can&#8217;t exceed 3 digits (999ms)</p>
<p>  ADMUX &amp;= ~((1&lt;&lt;MUX0)|(1&lt;&lt;MUX1)|(1&lt;&lt;MUX2));  //set up for ADC8 (temp)<br />
  ADMUX |= (1&lt;&lt;MUX3);<br />
  ADMUX |= (1&lt;&lt;REFS0)|(1&lt;&lt;REFS1);  //1.1V ADC reference<br />
  ADMUX &amp;= ~(1&lt;&lt;ADLAR); //undo left adjust result for 8 bit ADC <br />
  ADCSRA |= (1&lt;&lt;ADSC); //start conversion<br />
  while(ADCSRA &amp; (1&lt;&lt;ADSC)){} //wait for conversion<br />
  temperature= ADC;<br />
  temperature/= 12;  //scale temp value to degrees C <br />
  ADMUX |= (1&lt;&lt;ADLAR);  //restore left adjust result <br />
  ADMUX &amp;= ~(1&lt;&lt;REFS1);  //restore VCC ADC reference</p>
<p>  if(power!=old_power) updateLCD(); //update LCD if values changed<br />
  else if(duration!=old_duration) updateLCD();<br />
  if((!(PINB &amp; (1&lt;&lt;PINB0))) &amp; (duration&gt;0)) //check for footswitch input<br />
  {           <br />
   char j= 0;</p>
<p>      for(j= 0;j&lt; 3;)  //three sample noise filter<br />
   {<br />
    if(!(PINB &amp; (1&lt;&lt;PINB0))) j++;  //increment loop value if PORTC2 (fsw) is low<br />
    else j= 4;  //break out of the loop if high<br />
      }  <br />
                        <br />
      if(j==3)  //should only get here if we got three low samples<br />
   {<br />
    weld_message();<br />
    ADMUX |= (1&lt;&lt;MUX0)|(1&lt;&lt;MUX1)|(1&lt;&lt;MUX2); //sample ADC7 (current sensor)<br />
    ADMUX &amp;= ~(1&lt;&lt;MUX3);<br />
    ADCSRA |= (1&lt;&lt;ADATE); //ADC free runnnng mode<br />
    ADCSRA |= (1&lt;&lt;ADSC); //start conversion<br />
    _delay_us(5); //wait for ADC&#8217;s first reading<br />
    max_current= 0&#215;0000;  //reset max current from last cycle<br />
    sei();<br />
    while((!(PINB &amp; (1&lt;&lt;PINB0))) &amp; (duration&gt;0)){}  //wait for zero cross<br />
    cli();<br />
    ADCSRA &amp;= ~(1&lt;&lt;ADATE);  //turn off free running<br />
    switch_up(); //wait for footswitch release<br />
    updateLCD();<br />
   }<br />
  } <br />
 }<br />
}</p>
<p> <br />
ISR (ANALOG_COMP_vect)<br />
{ <br />
 char i= 0;</p>
<p>    for(i= 0;i&lt; 3;)  //three sample noise filter<br />
 {<br />
  if(!(ACSR &amp; (1&lt;&lt;ACO))) i++;  //increment loop value if ACO is low<br />
  else i= 4;  //break out of the loop if high<br />
    }  <br />
                        <br />
    if(i==3)  //should only get here if we got three low samples,  <br />
 {    //which indicates a zero crossing.  <br />
  TCNT1= 0;<br />
  while(TCNT1&lt; power){} //wait for phase rotation<br />
  PORTC |= (1&lt;&lt;PORTC3); //fire SCR<br />
  while(TCNT1&lt; 63333)  //wait for 7.9ms, 95% of one half cycle (60Hz)<br />
  {<br />
   current= ADCH;<br />
   if(current&gt;max_current) max_current= current;  //record the highest value<br />
  } <br />
  PORTC &amp;= ~(1&lt;&lt;PORTC3);  //turn off SCR gate<br />
  duration&#8211;;<br />
 }<br />
}</p>
<p> </p>
<p>void initLCD(void)<br />
{<br />
 _delay_ms(250);  // Wait for HD44780<br />
 PORTD &amp;= ~(1&lt;&lt;PORTD4);<br />
 PORTD |= (1&lt;&lt;PORTD1);<br />
 PORTD |= (1&lt;&lt;PORTD0);  <br />
 PORTD |= (1&lt;&lt;PORTD5); // function set<br />
 _delay_ms(2);<br />
 PORTD &amp;= ~(1&lt;&lt;PORTD5);<br />
 _delay_ms(20);  <br />
 PORTD |= (1&lt;&lt;PORTD5); // function set<br />
 _delay_ms(2);<br />
 PORTD &amp;= ~(1&lt;&lt;PORTD5);<br />
 _delay_ms(10);  <br />
 PORTD |= (1&lt;&lt;PORTD5); // function set<br />
 _delay_ms(2);<br />
 PORTD &amp;= ~(1&lt;&lt;PORTD5);<br />
 _delay_ms(10);<br />
 PORTD &amp;= ~(1&lt;&lt;PORTD0);<br />
 PORTD |= (1&lt;&lt;PORTD5); // initialize to 4 bit<br />
 _delay_ms(2);<br />
 PORTD &amp;= ~(1&lt;&lt;PORTD5);<br />
 _delay_ms(10);</p>
<p> lcd_write_byte(0,0&#215;28);  //set interface width, # of lines, and font size<br />
 lcd_write_byte(0,0&#215;0C);  //display on<br />
 lcd_write_byte(0,0&#215;01);  //clear display<br />
 lcd_write_byte(0,0&#215;06);  //increment address by one, shift cursor at write<br />
}</p>
<p> </p>
<p>void lcd_write_byte(unsigned char CONTROL, unsigned char DATA)<br />
{<br />
 if(CONTROL == 1) PORTD |= (1&lt;&lt;PORTD4); else PORTD &amp;= ~(1&lt;&lt;PORTD4);<br />
 if((DATA &amp; 0&#215;80) == 0&#215;80) PORTD |= (1&lt;&lt;PORTD3); else PORTD &amp;= ~(1&lt;&lt;PORTD3);<br />
 if((DATA &amp; 0&#215;40) == 0&#215;40) PORTD |= (1&lt;&lt;PORTD2); else PORTD &amp;= ~(1&lt;&lt;PORTD2);<br />
 if((DATA &amp; 0&#215;20) == 0&#215;20) PORTD |= (1&lt;&lt;PORTD1); else PORTD &amp;= ~(1&lt;&lt;PORTD1);<br />
 if((DATA &amp; 0&#215;10) == 0&#215;10) PORTD |= (1&lt;&lt;PORTD0); else PORTB &amp;= ~(1&lt;&lt;PORTD0);<br />
 PORTD |= (1&lt;&lt;PORTD5);<br />
 _delay_ms(1);<br />
 PORTD &amp;= ~(1&lt;&lt;PORTD5);</p>
<p> if((DATA &amp; 0&#215;08) == 0&#215;08) PORTD |= (1&lt;&lt;PORTD3); else PORTD &amp;= ~(1&lt;&lt;PORTD3);<br />
 if((DATA &amp; 0&#215;04) == 0&#215;04) PORTD |= (1&lt;&lt;PORTD2); else PORTD &amp;= ~(1&lt;&lt;PORTD2);<br />
 if((DATA &amp; 0&#215;02) == 0&#215;02) PORTD |= (1&lt;&lt;PORTD1); else PORTD &amp;= ~(1&lt;&lt;PORTD1);<br />
 if((DATA &amp; 0&#215;01) == 0&#215;01) PORTD |= (1&lt;&lt;PORTD0); else PORTD &amp;= ~(1&lt;&lt;PORTD0);<br />
 PORTD |= (1&lt;&lt;PORTD5);<br />
 _delay_ms(2);<br />
 PORTD &amp;= ~(1&lt;&lt;PORTD5);<br />
 _delay_ms(10);<br />
}</p>
<p> </p>
<p>void updateLCD(void)<br />
{<br />
 unsigned int temp_duration, temp_power, temp_max_current;<br />
 <br />
 temp_duration= (duration*8); //scale duration for BCD conversion to milliseconds          //<br />
 unsigned int duration_BCD= ((((temp_duration/10)+((temp_duration/100)*6))*16)+(temp_duration%10));<br />
 <br />
 unsigned char ONES= 0&#215;00;<br />
 unsigned char TENS= 0&#215;00;<br />
 unsigned char HUND= 0&#215;00;</p>
<p> if((duration_BCD &amp; 0&#215;0800) == 0&#215;0800) HUND |= 0&#215;08; else HUND &amp;= ~0&#215;08;<br />
 if((duration_BCD &amp; 0&#215;0400) == 0&#215;0400) HUND |= 0&#215;04; else HUND &amp;= ~0&#215;04;<br />
 if((duration_BCD &amp; 0&#215;0200) == 0&#215;0200) HUND |= 0&#215;02; else HUND &amp;= ~0&#215;02;<br />
 if((duration_BCD &amp; 0&#215;0100) == 0&#215;0100) HUND |= 0&#215;01; else HUND &amp;= ~0&#215;01;</p>
<p> if((duration_BCD &amp; 0&#215;0080) == 0&#215;0080) TENS |= 0&#215;08; else TENS &amp;= ~0&#215;08;<br />
 if((duration_BCD &amp; 0&#215;0040) == 0&#215;0040) TENS |= 0&#215;04; else TENS &amp;= ~0&#215;04;<br />
 if((duration_BCD &amp; 0&#215;0020) == 0&#215;0020) TENS |= 0&#215;02; else TENS &amp;= ~0&#215;02;<br />
 if((duration_BCD &amp; 0&#215;0010) == 0&#215;0010) TENS |= 0&#215;01; else TENS &amp;= ~0&#215;01;</p>
<p> if((duration_BCD &amp; 0&#215;0008) == 0&#215;0008) ONES |= 0&#215;08; else ONES &amp;= ~0&#215;08;<br />
 if((duration_BCD &amp; 0&#215;0004) == 0&#215;0004) ONES |= 0&#215;04; else ONES &amp;= ~0&#215;04;<br />
 if((duration_BCD &amp; 0&#215;0002) == 0&#215;0002) ONES |= 0&#215;02; else ONES &amp;= ~0&#215;02;<br />
 if((duration_BCD &amp; 0&#215;0001) == 0&#215;0001) ONES |= 0&#215;01; else ONES &amp;= ~0&#215;01;</p>
<p> ONES |= 0&#215;30;<br />
 TENS |= 0&#215;30;<br />
 HUND |= 0&#215;30;</p>
<p> lcd_write_byte(0&#215;00, 0&#215;01); //clear screen<br />
 lcd_write_byte(0&#215;01, 0&#215;20); //space<br />
 lcd_write_byte(0&#215;01, 0&#215;20); //space<br />
 lcd_write_byte(0&#215;01, 0&#215;20); //space<br />
 lcd_write_byte(0&#215;01, HUND);<br />
 lcd_write_byte(0&#215;01, TENS);<br />
 lcd_write_byte(0&#215;01, ONES);<br />
 lcd_write_byte(0&#215;01, 0&#215;20); //space<br />
 lcd_write_byte(0&#215;01, 0&#215;6D); //&#8217;m&#8217;<br />
 lcd_write_byte(0&#215;01, 0&#215;69); //&#8217;i&#8217;<br />
 lcd_write_byte(0&#215;01, 0&#215;6C); //&#8217;l&#8217;<br />
 lcd_write_byte(0&#215;01, 0&#215;6C); //&#8217;l&#8217;<br />
 lcd_write_byte(0&#215;01, 0&#215;69); //&#8217;i&#8217;<br />
 lcd_write_byte(0&#215;01, 0&#215;73); //&#8217;s&#8217;<br />
 lcd_write_byte(0&#215;01, 0&#215;65); //&#8217;e&#8217;<br />
 lcd_write_byte(0&#215;01, 0&#215;63); //&#8217;c&#8217;<br />
 lcd_write_byte(0&#215;01, 0&#215;6F); //&#8217;o&#8217;<br />
 lcd_write_byte(0&#215;01, 0&#215;6E); //&#8217;n&#8217;<br />
 lcd_write_byte(0&#215;01, 0&#215;64); //&#8217;d&#8217;<br />
 lcd_write_byte(0&#215;01, 0&#215;73); //&#8217;s&#8217;<br />
 temp_power= (power/665);  //scale power for BCD conversion to percent<br />
 temp_power=(95-temp_power);<br />
 unsigned int power_BCD= ((temp_power/10)*16)+(temp_power%10);</p>
<p> ONES= 0&#215;00;<br />
 TENS= 0&#215;00;</p>
<p> if((power_BCD &amp; 0&#215;0080) == 0&#215;0080) TENS |= 0&#215;08; else TENS &amp;= ~0&#215;08;<br />
 if((power_BCD &amp; 0&#215;0040) == 0&#215;0040) TENS |= 0&#215;04; else TENS &amp;= ~0&#215;04;<br />
 if((power_BCD &amp; 0&#215;0020) == 0&#215;0020) TENS |= 0&#215;02; else TENS &amp;= ~0&#215;02;<br />
 if((power_BCD &amp; 0&#215;0010) == 0&#215;0010) TENS |= 0&#215;01; else TENS &amp;= ~0&#215;01;</p>
<p> if((power_BCD &amp; 0&#215;0008) == 0&#215;0008) ONES |= 0&#215;08; else ONES &amp;= ~0&#215;08;<br />
 if((power_BCD &amp; 0&#215;0004) == 0&#215;0004) ONES |= 0&#215;04; else ONES &amp;= ~0&#215;04;<br />
 if((power_BCD &amp; 0&#215;0002) == 0&#215;0002) ONES |= 0&#215;02; else ONES &amp;= ~0&#215;02;<br />
 if((power_BCD &amp; 0&#215;0001) == 0&#215;0001) ONES |= 0&#215;01; else ONES &amp;= ~0&#215;01;</p>
<p> ONES |= 0&#215;30;<br />
 TENS |= 0&#215;30;</p>
<p> lcd_write_byte(0&#215;00, 0xC0); //go to second line<br />
 lcd_write_byte(0&#215;01, 0&#215;20); //space<br />
 lcd_write_byte(0&#215;01, 0&#215;20); //space<br />
 lcd_write_byte(0&#215;01, TENS);<br />
 lcd_write_byte(0&#215;01, ONES);<br />
 lcd_write_byte(0&#215;01, 0&#215;25); //&#8217;%&#8217;<br />
 lcd_write_byte(0&#215;01, 0&#215;20); //space<br />
 lcd_write_byte(0&#215;01, 0&#215;6F); //&#8217;o&#8217;<br />
 lcd_write_byte(0&#215;01, 0&#215;66); //&#8217;f&#8217;<br />
 lcd_write_byte(0&#215;01, 0&#215;20); //space<br />
 lcd_write_byte(0&#215;01, 0&#215;70); //&#8217;p&#8217;<br />
 lcd_write_byte(0&#215;01, 0&#215;68); //&#8217;h&#8217;<br />
 lcd_write_byte(0&#215;01, 0&#215;61); //&#8217;a&#8217;<br />
 lcd_write_byte(0&#215;01, 0&#215;73); //&#8217;s&#8217;<br />
 lcd_write_byte(0&#215;01, 0&#215;65); //&#8217;e&#8217;<br />
 temp_max_current= max_current;<br />
 temp_max_current-= 0&#215;7F;  //remove 2.5V sensor offset<br />
 temp_max_current*= 3; //scaling<br />
 temp_max_current/= 2; //scaling</p>
<p> unsigned int max_current_BCD= ((((temp_max_current/10)+((temp_max_current/100)*6))*16)+(temp_max_current%10));<br />
 <br />
 ONES= 0&#215;00;<br />
 TENS= 0&#215;00;<br />
 HUND= 0&#215;00;</p>
<p> if((max_current_BCD &amp; 0&#215;0800) == 0&#215;0800) HUND |= 0&#215;08; else HUND &amp;= ~0&#215;08;<br />
 if((max_current_BCD &amp; 0&#215;0400) == 0&#215;0400) HUND |= 0&#215;04; else HUND &amp;= ~0&#215;04;<br />
 if((max_current_BCD &amp; 0&#215;0200) == 0&#215;0200) HUND |= 0&#215;02; else HUND &amp;= ~0&#215;02;<br />
 if((max_current_BCD &amp; 0&#215;0100) == 0&#215;0100) HUND |= 0&#215;01; else HUND &amp;= ~0&#215;01;</p>
<p> if((max_current_BCD &amp; 0&#215;0080) == 0&#215;0080) TENS |= 0&#215;08; else TENS &amp;= ~0&#215;08;<br />
 if((max_current_BCD &amp; 0&#215;0040) == 0&#215;0040) TENS |= 0&#215;04; else TENS &amp;= ~0&#215;04;<br />
 if((max_current_BCD &amp; 0&#215;0020) == 0&#215;0020) TENS |= 0&#215;02; else TENS &amp;= ~0&#215;02;<br />
 if((max_current_BCD &amp; 0&#215;0010) == 0&#215;0010) TENS |= 0&#215;01; else TENS &amp;= ~0&#215;01;</p>
<p> if((max_current_BCD &amp; 0&#215;0008) == 0&#215;0008) ONES |= 0&#215;08; else ONES &amp;= ~0&#215;08;<br />
 if((max_current_BCD &amp; 0&#215;0004) == 0&#215;0004) ONES |= 0&#215;04; else ONES &amp;= ~0&#215;04;<br />
 if((max_current_BCD &amp; 0&#215;0002) == 0&#215;0002) ONES |= 0&#215;02; else ONES &amp;= ~0&#215;02;<br />
 if((max_current_BCD &amp; 0&#215;0001) == 0&#215;0001) ONES |= 0&#215;01; else ONES &amp;= ~0&#215;01;</p>
<p> ONES |= 0&#215;30;<br />
 TENS |= 0&#215;30;<br />
 HUND |= 0&#215;30;</p>
<p> lcd_write_byte(0&#215;00, 0&#215;95); //go to third line (DDRAM address 0&#215;15)<br />
 lcd_write_byte(0&#215;01, 0&#215;20); //space<br />
 lcd_write_byte(0&#215;01, HUND);<br />
 lcd_write_byte(0&#215;01, TENS);<br />
 lcd_write_byte(0&#215;01, ONES);<br />
 lcd_write_byte(0&#215;01, 0&#215;20); //space<br />
 lcd_write_byte(0&#215;01, 0&#215;61); //&#8217;a&#8217;<br />
 lcd_write_byte(0&#215;01, 0&#215;6D); //&#8217;m&#8217;<br />
 lcd_write_byte(0&#215;01, 0&#215;70); //&#8217;p&#8217;<br />
 lcd_write_byte(0&#215;01, 0&#215;73); //&#8217;s&#8217;<br />
 lcd_write_byte(0&#215;01, 0&#215;20); //space<br />
 lcd_write_byte(0&#215;01, 0&#215;28); //&#8217;(&#8217;<br />
 lcd_write_byte(0&#215;01, 0&#215;6D); //&#8217;m&#8217;<br />
 lcd_write_byte(0&#215;01, 0&#215;61); //&#8217;a&#8217;<br />
 lcd_write_byte(0&#215;01, 0&#215;78); //&#8217;x&#8217;<br />
 lcd_write_byte(0&#215;01, 0&#215;29); //&#8217;)&#8217;</p>
<p> unsigned int temperature_BCD= ((temperature/10)*16)+(temperature%10);<br />
 <br />
 ONES= 0&#215;00;<br />
 TENS= 0&#215;00;</p>
<p> if((temperature_BCD &amp; 0&#215;0080) == 0&#215;0080) TENS |= 0&#215;08; else TENS &amp;= ~0&#215;08;<br />
 if((temperature_BCD &amp; 0&#215;0040) == 0&#215;0040) TENS |= 0&#215;04; else TENS &amp;= ~0&#215;04;<br />
 if((temperature_BCD &amp; 0&#215;0020) == 0&#215;0020) TENS |= 0&#215;02; else TENS &amp;= ~0&#215;02;<br />
 if((temperature_BCD &amp; 0&#215;0010) == 0&#215;0010) TENS |= 0&#215;01; else TENS &amp;= ~0&#215;01;</p>
<p> if((temperature_BCD &amp; 0&#215;0008) == 0&#215;0008) ONES |= 0&#215;08; else ONES &amp;= ~0&#215;08;<br />
 if((temperature_BCD &amp; 0&#215;0004) == 0&#215;0004) ONES |= 0&#215;04; else ONES &amp;= ~0&#215;04;<br />
 if((temperature_BCD &amp; 0&#215;0002) == 0&#215;0002) ONES |= 0&#215;02; else ONES &amp;= ~0&#215;02;<br />
 if((temperature_BCD &amp; 0&#215;0001) == 0&#215;0001) ONES |= 0&#215;01; else ONES &amp;= ~0&#215;01;</p>
<p> ONES |= 0&#215;30;<br />
 TENS |= 0&#215;30;</p>
<p> lcd_write_byte(0&#215;00, 0xD5); //go to fourth line (DDRAM address 0&#215;55)<br />
 lcd_write_byte(0&#215;01, 0&#215;20); //space<br />
 lcd_write_byte(0&#215;01, TENS);<br />
 lcd_write_byte(0&#215;01, ONES);<br />
 lcd_write_byte(0&#215;01, 0xDF); //degree symbol<br />
 lcd_write_byte(0&#215;01, 0&#215;43); //&#8217;C&#8217;<br />
 lcd_write_byte(0&#215;01, 0&#215;20); //space<br />
 lcd_write_byte(0&#215;01, 0&#215;63); //&#8217;c&#8217;<br />
 lcd_write_byte(0&#215;01, 0&#215;61); //&#8217;a&#8217;<br />
 lcd_write_byte(0&#215;01, 0&#215;73); //&#8217;s&#8217;<br />
 lcd_write_byte(0&#215;01, 0&#215;65); //&#8217;e&#8217;<br />
 lcd_write_byte(0&#215;01, 0&#215;20); //space<br />
 lcd_write_byte(0&#215;01, 0&#215;74); //&#8217;t&#8217;<br />
 lcd_write_byte(0&#215;01, 0&#215;65); //&#8217;e&#8217;<br />
 lcd_write_byte(0&#215;01, 0&#215;6D); //&#8217;m&#8217;<br />
 lcd_write_byte(0&#215;01, 0&#215;70); //&#8217;p&#8217;<br />
}</p>
<p> </p>
<p>void switch_up(void)<br />
{<br />
 if(!(PINB &amp; (1&lt;&lt;PINB0)))<br />
 {<br />
  lcd_write_byte(0&#215;00, 0&#215;01); //clear screen<br />
  lcd_write_byte(0&#215;01, 0&#215;20); //space<br />
  lcd_write_byte(0&#215;01, 0&#215;20); //space<br />
  lcd_write_byte(0&#215;01, 0&#215;72); //&#8217;r&#8217;<br />
  lcd_write_byte(0&#215;01, 0&#215;65); //&#8217;e&#8217;<br />
  lcd_write_byte(0&#215;01, 0&#215;6C); //&#8217;l&#8217;<br />
  lcd_write_byte(0&#215;01, 0&#215;65); //&#8217;e&#8217;<br />
  lcd_write_byte(0&#215;01, 0&#215;61); //&#8217;a&#8217;<br />
  lcd_write_byte(0&#215;01, 0&#215;73); //&#8217;s&#8217;<br />
  lcd_write_byte(0&#215;01, 0&#215;65); //&#8217;e&#8217;<br />
  lcd_write_byte(0&#215;01, 0&#215;20); //space<br />
  lcd_write_byte(0&#215;01, 0&#215;66); //&#8217;f&#8217;<br />
  lcd_write_byte(0&#215;01, 0&#215;6F); //&#8217;o&#8217;<br />
  lcd_write_byte(0&#215;01, 0&#215;6F); //&#8217;o&#8217;<br />
  lcd_write_byte(0&#215;01, 0&#215;74); //&#8217;t&#8217;<br />
  lcd_write_byte(0&#215;01, 0&#215;73); //&#8217;s&#8217;<br />
  lcd_write_byte(0&#215;01, 0&#215;77); //&#8217;w&#8217;<br />
  lcd_write_byte(0&#215;01, 0&#215;69); //&#8217;i&#8217;<br />
  lcd_write_byte(0&#215;01, 0&#215;74); //&#8217;t&#8217;<br />
  lcd_write_byte(0&#215;01, 0&#215;63); //&#8217;c&#8217;<br />
  lcd_write_byte(0&#215;01, 0&#215;68); //&#8217;h&#8217;<br />
  while(!(PINB &amp; (1&lt;&lt;PINB0))){};<br />
 }<br />
}</p>
<p> </p>
<p>void weld_message(void)<br />
{<br />
  lcd_write_byte(0&#215;00, 0&#215;01); //clear screen<br />
  lcd_write_byte(0&#215;01, 0&#215;20); //space<br />
  lcd_write_byte(0&#215;01, 0&#215;20); //space<br />
  lcd_write_byte(0&#215;01, 0&#215;77); //&#8217;w&#8217;<br />
  lcd_write_byte(0&#215;01, 0&#215;65); //&#8217;e&#8217;<br />
  lcd_write_byte(0&#215;01, 0&#215;6C); //&#8217;l&#8217;<br />
  lcd_write_byte(0&#215;01, 0&#215;64); //&#8217;d&#8217;<br />
  lcd_write_byte(0&#215;01, 0&#215;69); //&#8217;i&#8217;<br />
  lcd_write_byte(0&#215;01, 0&#215;6E); //&#8217;n&#8217;<br />
  lcd_write_byte(0&#215;01, 0&#215;67); //&#8217;g&#8217;<br />
  lcd_write_byte(0&#215;01, 0&#215;2E); //&#8217;.&#8217;<br />
  lcd_write_byte(0&#215;01, 0&#215;2E); //&#8217;.&#8217;<br />
  lcd_write_byte(0&#215;01, 0&#215;2E); //&#8217;.&#8217;<br />
}</p>
]]></content:encoded>
			<wfw:commentRss>http://www.imsolidstate.com/archives/590/feed</wfw:commentRss>
		<slash:comments>14</slash:comments>
		</item>
		<item>
		<title>MAX4173 Current sense amplifier</title>
		<link>http://www.imsolidstate.com/archives/561</link>
		<comments>http://www.imsolidstate.com/archives/561#comments</comments>
		<pubDate>Mon, 18 Jan 2010 21:35:48 +0000</pubDate>
		<dc:creator>imsolidstate</dc:creator>
				<category><![CDATA[Electronics]]></category>
		<category><![CDATA[Current sense]]></category>

		<guid isPermaLink="false">http://www.imsolidstate.com/?p=561</guid>
		<description><![CDATA[I just built a current sensing application based on a MAX4173. I am suitably impressed with this device. It is extremely versatile and cheap. The MAX4173 allows you to measure the current across a sense resistor with a 28V common-mode range and provides a ground-referenced output proportional to the current across the resistor. I will [...]]]></description>
			<content:encoded><![CDATA[<p>I just built a current sensing application based on a MAX4173. I am suitably impressed with this device. It is extremely versatile and cheap. The MAX4173 allows you to measure the current across a sense resistor with a 28V common-mode range and provides a ground-referenced output proportional to the current across the resistor. I will probably be using one on most of my projects from now on since I do a lot of prototype work and having a cheap and easy way to sense current draw is pretty useful.</p>
<p><img class="alignnone size-large wp-image-563" title="MAX4173" src="http://www.imsolidstate.com/wp-content/uploads/2010/01/IMAG0028-1024x768.jpg" alt="IMAG0028" width="645" height="484" /></p>
<p>The MAX4173 is available in either an SOT23 or SO8 package, and only requires a decoupling capacitor and a sense resistor to operate. There are three different preset gains available so by combining that with the sense resistor value you can achieve a variety of current ranges.<br />
I used the device in a 4-20mA control loop, with a 20X gain and a 10Ω resistor for a 0.8-4V output into an ATtiny 13&#8217;s ADC with a 5V reference. The 10Ω sense resistor is small enough that the loop won&#8217;t be affected and large enough to provide good signal range and sensitivity.<br />
For future projects I will probably be using the higher gain version with a smaller sense resistor on the input power line and just leave the MAX4173 output on a header for diagnostic measurement if I don&#8217;t have a spare ADC pin. This will limit circuit loading but provide an output for measuring actual circuit current draw.<br />
There is another similar version of the MAX4173, the MAX4080. It can handle higher common-mode voltages and bidirectional current. It&#8217;s also a bit more accurate than the 4173, but I haven&#8217;t tried it out yet.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.imsolidstate.com/archives/561/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>LED backlit desk sign</title>
		<link>http://www.imsolidstate.com/archives/575</link>
		<comments>http://www.imsolidstate.com/archives/575#comments</comments>
		<pubDate>Mon, 18 Jan 2010 18:20:12 +0000</pubDate>
		<dc:creator>imsolidstate</dc:creator>
				<category><![CDATA[Electronics]]></category>
		<category><![CDATA[Engraving]]></category>

		<guid isPermaLink="false">http://www.imsolidstate.com/?p=575</guid>
		<description><![CDATA[I was messing around a while ago with my CNC and some of the various engraving bits I&#8217;ve tried for routing PCB traces. I had a piece of acrylic lying around, so I made myself a sign for my desk at work. I don&#8217;t really have that job title, it&#8217;s technically &#8220;intern&#8221; since I&#8217;m doing [...]]]></description>
			<content:encoded><![CDATA[<p>I was messing around a while ago with my CNC and some of the various engraving bits I&#8217;ve tried for routing PCB traces. I had a piece of acrylic lying around, so I made myself a sign for my desk at work. I don&#8217;t really have that job title, it&#8217;s technically &#8220;intern&#8221; since I&#8217;m doing the college thing; but it <em>would</em> be my title if a job title was based on what you did. The description next to my name in the office&#8217;s roster is actually &#8220;special projects&#8221;, but it didn&#8217;t sound as interesting.</p>
<p><img class="alignnone size-large wp-image-576" title="LED backlit engraved sign" src="http://www.imsolidstate.com/wp-content/uploads/2010/01/IMAG0020-1024x768.jpg" alt="IMAG0020" width="645" height="484" /></p>
<p>I used a NUD4001 constant-current LED driver from ON Semiconductor and some OSRAM SideLEDs. The NUD4001 is really quick and easy to implement as it only requires an external resistor to set the current level. The unit is fused, and powered by a 9V AC/DC wall wart. I tried a couple different colors, but this amber looked the best.</p>
<p>As a side note, I really like using ON semiconductor parts. They have excellent datasheets that usually have really good examples.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.imsolidstate.com/archives/575/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Turn signals for a trailer</title>
		<link>http://www.imsolidstate.com/archives/542</link>
		<comments>http://www.imsolidstate.com/archives/542#comments</comments>
		<pubDate>Tue, 29 Dec 2009 21:22:25 +0000</pubDate>
		<dc:creator>imsolidstate</dc:creator>
				<category><![CDATA[6.0L Ford Super Duty]]></category>
		<category><![CDATA[Electronics]]></category>
		<category><![CDATA[6.0]]></category>
		<category><![CDATA[AVR]]></category>
		<category><![CDATA[Powerstroke]]></category>
		<category><![CDATA[Trailer]]></category>
		<category><![CDATA[Truck]]></category>

		<guid isPermaLink="false">http://www.imsolidstate.com/?p=542</guid>
		<description><![CDATA[I&#8217;ve been working intermittently on a project to come up with a way to add turn signals to the side of a trailer without needing any extra wiring from the truck. The way people normally do this is by tapping the side markers into the turn signal wires. However, that makes the side markers turn [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve been working intermittently on a project to come up with a way to add turn signals to the side of a trailer without needing any extra wiring from the truck. The way people normally do this is by tapping the side markers into the turn signal wires. However, that makes the side markers turn on and off with the brakes which makes for a confusing signal. There is no shortage of patents for adding this type of capability, but they usually require a unique transmitter aboard the truck that communicates with another unique receiver on the trailer. Since the 6 pin standard is ubiquitous, that type of system will likely not be available or popular for some time. I didn&#8217;t want to add any special hardware to the truck either. So I&#8217;ve been working on a solution that taps into the trailer&#8217;s main electrical harness and creates an extra side turn signal for each side of the trailer and also functions as a clearance light.</p>
<p><img class="alignnone size-large wp-image-543" title="Turn Signal Controller" src="http://www.imsolidstate.com/wp-content/uploads/2009/12/Brake-012-1024x768.jpg" alt="Turn Signal Controller" width="645" height="484" /></p>
<p>You can use any standard clearance light and it will function as a clearance light when the lights are on, and also flash that light when the appropriate turn signal is on. The brakes will not cause the side markers to flash. You can add new lights, or simply intercept the wiring to existing clearance lights. The only modification is to the trailer, and it will work the same for any truck. The only requirement is that the trailer has electric brakes and the truck has a brake controller.</p>
<p>My code is running on an ATtiny24, on a board I prototyped just for this. I built an in-line harness for testing that passes the signals from the 6 pin connector to the trailer, but taps into those signals for the logic. My device has two standard LED clearance lamps attached as left and right turn signals for testing. This system is an add-on to the existing trailer wiring, so in the event of a failure nothing is compromised.</p>
<p><img class="alignnone size-large wp-image-544" title="Turn Signal Controller with ISP" src="http://www.imsolidstate.com/wp-content/uploads/2009/12/Brake-007-1024x768.jpg" alt="Turn Signal Controller with ISP" width="645" height="484" /></p>
<p>It has been tough getting this working, as there&#8217;s not exactly a manual for how the truck and the brake controller handle the signals. It&#8217;s taken a few months, as evidenced by the tarnished PCB. If I remember right I made it this summer.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.imsolidstate.com/archives/542/feed</wfw:commentRss>
		<slash:comments>15</slash:comments>
		</item>
	</channel>
</rss>
