43oh

# swampdonkeykami

Members

52

2

## Reputation Activity

1.
"@swampdonkeykami  Thank you for the review!"

You may be mistaking me for somebody with an obscure youtube channel.
2.
Had to wrassle with paypal, but it's paid now.
3. swampdonkeykami got a reaction from Rickta59 in CHEAP air cylinder positioning with 43oh and flow meter
Hi Guys! I've been toying with an idea, that I  didn't think would work, and set up a test jig.
Using an air compressor, a solenoid valve, a flow meter and a 43oh I found that we can get a repeatable cylinder position, like +-2.5 mm!
The flow meter is measuring the volume of air that we've fed to the cylinder and the 43oh opens or closes a solenoid valve depending on the cylinder position.http://arduinoforgoodnotevil.blogspot.ca/2013/10/accurate-ish-pneumatic-cylinder.html

4. swampdonkeykami got a reaction from chibiace in CHEAP air cylinder positioning with 43oh and flow meter
Hi Guys! I've been toying with an idea, that I  didn't think would work, and set up a test jig.
Using an air compressor, a solenoid valve, a flow meter and a 43oh I found that we can get a repeatable cylinder position, like +-2.5 mm!
The flow meter is measuring the volume of air that we've fed to the cylinder and the 43oh opens or closes a solenoid valve depending on the cylinder position.http://arduinoforgoodnotevil.blogspot.ca/2013/10/accurate-ish-pneumatic-cylinder.html

5. swampdonkeykami got a reaction from bluehash in CHEAP air cylinder positioning with 43oh and flow meter
Hi Guys! I've been toying with an idea, that I  didn't think would work, and set up a test jig.
Using an air compressor, a solenoid valve, a flow meter and a 43oh I found that we can get a repeatable cylinder position, like +-2.5 mm!
The flow meter is measuring the volume of air that we've fed to the cylinder and the 43oh opens or closes a solenoid valve depending on the cylinder position.http://arduinoforgoodnotevil.blogspot.ca/2013/10/accurate-ish-pneumatic-cylinder.html

6. swampdonkeykami got a reaction from multivac in The Cramp! 430 powered desktop crane lamp
Even more impressive! Thanks for showing us what great projects can be accomplished even without all the tools. Too often people put up false barriers; "I could do that, if only I had a 3D printer. If only I had a CNC mill. If only I could program..."

:thumbup: Well done sir!
7.
I have to apologize about writing "but of course you can't use the communication examples."
@@Rickta59 pointed out that TimerSerial.h implements a Timer/software driven UART. This works on the g2231 and g2452 and is automatically configure in Serial.h

Yes you'd have better luck with TI's Code Composer studio for chips not supported in energia
8.
A few days ago I was doing some testing and programed a MSP430F2013 using a ez430-f2013 but only uploaded a "blink" sketch. I select the board "LaunchPad w/ MSP430G2231 1Mhz".
If you look in the devices datasheets you'll see that both have the configurations registers (at least the for Port1 and 2) at the same address. Also both chips have the same device ID, so I'm thinking that some code will be compatible.
As semicolo said you program the chips using the Spy-By-Wire interface (that is RESET and TEST) it has nothing to do with the UART
9. swampdonkeykami got a reaction from multivac in The Cramp! 430 powered desktop crane lamp
Man! You must have a lot more patience than I do!
When you build V. 2 :thumbup: ; I'll send you some copper coated steel rod. It's filler rod for tig welding, but the copper coating will make it easy to solder/braze.
10.
hehe, too bad i dont have a pic of the wire rolls before. i straightened it by pulling it tight against a fixed metal tube(my workbench`s leg), which gave me a kink free, but slightly curved wire. I made a jig to put the three main booms together, so that went fast. the chassis, roof, and the two tiny thingies up top where made without support, and took some time.
11. swampdonkeykami got a reaction from multivac in The Cramp! 430 powered desktop crane lamp
Wah?! That's great, you did a great job getting the wire straightened!
12.
//******************************************************************************
//
//  Electronic Fuel Injection, Written by Jack Murray  2011
//    NOTE, this is not cleaned up finished code, but decided to share it anyway.
//
//  We use a Geo Metro 1.0L Throttle Body Injector (TBI) for fuel
//  The throttle is an electronic controlled from a 2004 Prius
//  The water temp sensor is from a '87 Chevy
//  The 02 sensor is a generic narrow band.
//  We use the on-chip temp sensor to proxy the air temp
//  The Fuel Pump is from an '89 Jeep Cherokee, and is turned on through
//  a relay from a Geo Metro.
//
//  The EFI control is simply to determine how much time to turn on the injector,
//  which is how long to make the pulse widths.
//
//  For the tractor, we also control the throttle, and try to maintain a
//  given RPM that is determined by the RPM adjustment knob
//
//
//  The controller is a repurposed MSP4301232 board that was a battery charger.
//  It has a two line LCD, one darlington, two mosfets and 8 10bit ADC inputs
//  We use the darlington to turn on the fuel pump relay, which is P1.1
//  Mosfet A is used to for the fuel injector driver, P1.3 TA2
//  Mosfet B is used to control the throttle, P1.2 TA1
//    A0   -- user interface selector
//    A1   o2 sensor
//    A2   -- lcd dial
//    A3   water temp
//    A5   -- connected to lcd
//    A6   MAP sensor
//    A7   throttle position sensor from prius
//
//    P2.5 is the tach input, that goes low on an ignition event
//
//******************************************************************************

#include  <signal.h>
#include  <io.h>

// efi settings, we need to limit this to 128 bytes if possible
// so we can erase one segment and not lose everything if something goes wrong
// during the update.
struct ConfigEFI {
unsigned char valid;   // whether containts are valid
unsigned char airTable[32];   // PWM Adjustment for air temperature
unsigned char watTable[32];   // PWM Adjustment for water temp
unsigned char rpmTable[32];   // PWM Adjustment for rpm
};

// Copy of current config parameters in Memory
struct ConfigEFI Config, *FConfig;

unsigned short selectime;
unsigned short selecting;
unsigned short dstate, lastSeconds, seconds;
unsigned short lastdial, dialpos;

unsigned short throttleTimeOn, throttleTimeOff, throttleIsOn;
unsigned short injectorIsOn, injectorStartTime, injectorPulseWidth;
unsigned short injectorBaseWidth;
unsigned short injectorTimeOff, injectorTimeOn;

unsigned short dial, tps;
unsigned short targetTPS = 500;

unsigned short tach, rpm, lastTach;

unsigned short airSensor;
unsigned short waterSensor;
unsigned short mapSensor;
unsigned short o2Sensor;

void startInjector();
void updateInjectorWidth();
void updateThrottle();

#define Reboot()     WDTCTL = 0xFFFF        // cause reset from invalid WDT password

flashcopy(char *mptr, char *fptr, int size)
{
int i;
for (i=0; i<size; i++)
{
asm("dint");
FCTL3 = FWKEY;                  // Clear Lock bit
FCTL1 = FWKEY + WRT;            // Set WRT bit for write operation
*fptr++ = *mptr++;         // Write value to flash
FCTL1 = FWKEY;                  // Clear WRT bit
FCTL3 = FWKEY + LOCK;           // Reset LOCK bit
asm("eint");
}
}

// We save configs to flash.  We can only erase an entire segment,
// so we first erase all of Segment A, copy the Segment B values + New values into A,
// then erase B, and then copy A into B.  B now has the updated values.
//
SaveConfig()
{
int c,i;
char *mptr;
char *fptr;
int cfgsize = sizeof(Config);
struct ConfigEFI *Aptr, *Bptr;

Aptr = (struct ConfigEFI *)0x1080;
Bptr = (struct ConfigEFI *)0x1000;

WDTCTL = WDTPW + WDTHOLD;       // Stop watchdog timer
FCTL2 = FWKEY + FSSEL0 + FN4;   // 5Mhz MCLK/16 = 312Khz for Flash Timing Generator
//
// Erase Segment A
fptr = (char *)Aptr;   // Initialize Flash segment B pointer
asm("dint");
FCTL1 = FWKEY + ERASE;          // Set Erase bit
FCTL3 = FWKEY;                  // Clear Lock bit
*fptr = 0;                // Dummy write to erase Flash segment A
asm("eint");
//
// Write Segment A from memory
mptr = (char *)&Config;
flashcopy(mptr, (char *)Aptr, cfgsize);
//
// Now all of Erase B
fptr = (char *)Bptr;   // Initialize Flash segment B pointer
asm("dint");
FCTL1 = FWKEY + ERASE;          // Set Erase bit
FCTL3 = FWKEY;                  // Clear Lock bit
*fptr = 0;                // Dummy write to erase Flash segment A
asm("eint");
//
// Now copy all of A to B
flashcopy((char *)Aptr, (char *)Bptr, cfgsize);
// restart watchdog
// WDTCTL = WDT_ADLY_1000;               // Set Watchdog Timer interval back to 1000ms (1-sec)
}

{
unsigned short val;
ADC10CTL0 &= ~ENC;             // Disable while changing input channel
return val;
}

{
unsigned short i, val;
unsigned long avg;
avg = 0;
for(i=0; i<256; i++) {
}
val = avg >> 8;
return val;
}

unsigned short waterSensor;
unsigned short waterTemp;

{
unsigned short val, tmp1, tmp2, tmp3, tmp4;
//
// The water temp is a GM water temp sensor
// It has a log-scale of 10k at 0F down to 100 ohm at 250F
// We use a divider circuit with a 330ohm resistor,
// basically we just hardcode the readings to 32 levels
ADC10CTL0 =            REFON | REF2_5V | ADC10SHT_2 | SREF_1;   // turn OFF
waterSensor = (tmp1 + tmp2 + tmp3 + tmp4) >> 2;
// *=measured
// temp    res      volts   1.5v   2.5v
// 244 F : 110 ohm : 0.75v :  512 :  307
// 240 F : 120 ohm : 0.80v :  546 :  327
// 236 F : 130 ohm : 0.85v :  578 :  347
// 232 F : 140 ohm : 0.89v :  610 :  366
// 228 F : 150 ohm : 0.94v :  640 :  384
// 224 F : 160 ohm : 0.98v :  668 :  401
// 220 F : 170 ohm : 1.02v :  696 :  417
// 216 F : 180 ohm : 1.06v :  722 :  433
//*212 F : 190 ohm : 1.10v :  748 :  448
// 208 F : 200 ohm : 1.13v :  772 :  463
// 204 F : 210 ohm : 1.17v :  796 :  477
//*200 F : 220 ohm : 1.20v :  819 :  491
// 196 F : 236 ohm : 1.25v :  853 :  512
// 192 F : 252 ohm : 1.30v :  886 :  532
// 188 F : 268 ohm : 1.34v :  917 :  550
// 184 F : 284 ohm : 1.39v :  947 :  568
//*180 F : 300 ohm : 1.43v :  975 :  585
// 176 F : 324 ohm : 1.49v : 1014 :  608
// 172 F : 348 ohm : 1.54v : 1051 :  630
// 168 F : 372 ohm : 1.59v : 1085 :  651
// 164 F : 396 ohm : 1.64v : 1117 :  670
//*160 F : 420 ohm : 1.68v : 1146 :  688
// 156 F : 460 ohm : 1.75v : 1192 :  715
// 152 F : 500 ohm : 1.81v : 1233 :  740
// 148 F : 540 ohm : 1.86v : 1271 :  762
// 144 F : 580 ohm : 1.91v : 1305 :  783
//*140 F : 620 ohm : 1.96v : 1336 :  801
// 136 F : 660 ohm : 2.00v : 1365 :  819
// 132 F : 700 ohm : 2.04v : 1391 :  835
// 128 F : 740 ohm : 2.07v : 1416 :  849
// 124 F : 780 ohm : 2.11v : 1439 :  863
// 120 F : 820 ohm : 2.14v : 1460 :  876
if (waterSensor < 600) {  // if temp is > 180, we switch to using the 1.5v reference to get better resolution
ADC10CTL0 =            REFON | ADC10SHT_2 | SREF_1;   // use VCC ref, turn OFF
// switch it back to 2.5v
ADC10CTL0 =            REFON | REF2_5V | ADC10SHT_2 | SREF_1;   // turn OFF
waterSensor = (tmp1 + tmp2 + tmp3 + tmp4) >> 2;
if (waterSensor <= 512) { waterTemp = 32; }
else if (waterSensor <= 546) { waterTemp = 31; }
else if (waterSensor <= 578) { waterTemp = 30; }
else if (waterSensor <= 610) { waterTemp = 29; }
else if (waterSensor <= 640) { waterTemp = 28; }
else if (waterSensor <= 668) { waterTemp = 27; }
else if (waterSensor <= 696) { waterTemp = 26; }
else if (waterSensor <= 722) { waterTemp = 25; }
else if (waterSensor <= 748) { waterTemp = 24; }
else if (waterSensor <= 772) { waterTemp = 23; }
else if (waterSensor <= 796) { waterTemp = 22; }
else if (waterSensor <= 819) { waterTemp = 21; }
else if (waterSensor <= 853) { waterTemp = 20; }
else if (waterSensor <= 886) { waterTemp = 19; }
else if (waterSensor <= 917) { waterTemp = 18; }
else if (waterSensor <= 947) { waterTemp = 17; }
else if (waterSensor <= 975) { waterTemp = 16; }
else { waterTemp = 15; }
}
// 176 F : 324 ohm : 1.49v : 1014 :  608
// 172 F : 348 ohm : 1.54v : 1051 :  630
// 168 F : 372 ohm : 1.59v : 1085 :  651
// 164 F : 396 ohm : 1.64v : 1117 :  670
// 160 F : 420 ohm : 1.68v : 1146 :  688
// 156 F : 460 ohm : 1.75v : 1192 :  715
// 152 F : 500 ohm : 1.81v : 1233 :  740
// 148 F : 540 ohm : 1.86v : 1271 :  762
// 144 F : 580 ohm : 1.91v : 1305 :  783
// 140 F : 620 ohm : 1.96v : 1336 :  801
// 136 F : 660 ohm : 2.00v : 1365 :  819
// 132 F : 700 ohm : 2.04v : 1391 :  835
// 128 F : 740 ohm : 2.07v : 1416 :  849
// 124 F : 780 ohm : 2.11v : 1439 :  863
// 120 F : 820 ohm : 2.14v : 1460 :  876
else if (waterSensor <= 608) { waterTemp = 15; }
else if (waterSensor <= 630) { waterTemp = 14; }
else if (waterSensor <= 651) { waterTemp = 13; }
else if (waterSensor <= 670) { waterTemp = 12; }
else if (waterSensor <= 688) { waterTemp = 11; }
else if (waterSensor <= 715) { waterTemp = 10; }
else if (waterSensor <= 740) { waterTemp = 9; }
else if (waterSensor <= 762) { waterTemp = 8; }
else if (waterSensor <= 783) { waterTemp = 7; }
else if (waterSensor <= 801) { waterTemp = 6; }
else if (waterSensor <= 819) { waterTemp = 5; }
else if (waterSensor <= 835) { waterTemp = 4; }
else if (waterSensor <= 849) { waterTemp = 3; }
else if (waterSensor <= 863) { waterTemp = 2; }
else if (waterSensor <= 1024) { waterTemp = 1; }
return waterTemp;
}

__attribute__ ((naked)) void delay() {
//first arg comes in register R15, loop uses 3 cycles per round
//the eight cycles come from the call overhead and ret
//delay_time = (1/MCLK)*(8+(3*n))
asm("xloop: dec r15\n  jnz xloop\n ret");
}

//  To send command, Set Enable to High, set RS+DB command, then Clear Enable
void
LCDout(int rs, int data)
{
unsigned char high4 = (data >> 4) & 0x0F;
unsigned char low4 = (data & 0x0F);
unsigned char status = 0;
unsigned char clear = 0;
unsigned short max = 0;

if (rs) {
P3OUT =  0x30;  // Set Enable + RS, clear data bits
P3OUT |= high4; // Set high 4-bits
P3OUT &= ~0x10;  // Clear Enable
P3OUT = 0x30;  // Set Enable + RS
P3OUT |= low4; // Set low 4-bits
P3OUT &= ~0x10;  // Clear Enable
}
else {
P3OUT = 0x10;  // Set Enable, Clear RS, Clear data bits
P3OUT |= high4; // Set high 4-bits
P3OUT &= ~0x10;  // Clear Enable
// Now write low4 bits
P3OUT = 0x10;  // Set Enable, Clear RS, Clear data bits
P3OUT |= low4; // Set low 4-bits
P3OUT &= ~0x10;  // Clear Enable
}

delay(750*5);

return;
}

void
LCDouts(char *str)
{
while (*str != 0) {
LCDout(1, *str++);        //write char and increment pointer, careful about 7-8 position!
}
}

// initialize LCD.
//  P3.0-1-2-3 connected to DB4-5-6-7
//  P3.4 is Enable, P3.5 is RS(RegisterSelect, Read/Write is wired to Zero/Ground.
void
initLCD()
{
unsigned int wait;
P3OUT = 0x00;    // Clear P3.0-7
P3DIR = 0x3F;    // Set P3.0-5 to output direction

P3OUT = 0x00;   // Clear Enable, RS, Data

P3OUT = 0x03;   // Set Data Cmd for 8bit mode
P3OUT |= 0x10;  // Set Enable.
P3OUT &= ~0x10;  // Clear Enable.
delay(750*20);

P3OUT = 0x03;   // Set Data Cmd
P3OUT |= 0x10;  // Set Enable.
P3OUT &= ~0x10;  // Clear Enable.
delay(750*20);

P3OUT = 0x03;   // Set Data Cmd
P3OUT |= 0x10;  // Set Enable.
P3OUT &= ~0x10;  // Clear Enable.
delay(750*20);

P3OUT = 0x02;   // Now set Data Cmd to 0x20 for switch to 4bit mode
P3OUT |= 0x10;  // Set Enable.
P3OUT &= ~0x10;  // Clear Enable.
delay(750*20);

// Now we are in 4bit mode.  Do command again, now in 4bit mode.

P3OUT = 0x02;   // Now send Function Set Data Cmd to 0x20 for switch to 4bit mode
P3OUT |= 0x10;  // Set Enable.
P3OUT &= ~0x10;  // Clear Enable.
P3OUT = 0x0C;   // Send lower 4bit for 2-lines
P3OUT |= 0x10;  // Set Enable.
P3OUT &= ~0x10;  // Clear Enable.
delay(750*20);

}

void
LCDdigit(unsigned char d) {
LCDout(1, '0'+ d);
}

unsigned short
BCDecode(unsigned short val, unsigned char *d1, unsigned char *d2, unsigned char *d3)
{
*d1 = val % 10;
val = val / 10;
*d2 = val % 10;
val = val / 10;
if (d3)
{ *d3 = val % 10;
val = val / 10;
}
return val;
}

// output charge time.  We increment only every 4 seconds, so 15 seconds is actually 60
// we divide by 15 to get minutes.
// 11.72v Ach 12:22
// 11.72v Ach   81F
LCDout_minutes (unsigned short ticks)
{
unsigned short val;
char d1,d2;

val = ticks/60;
BCDecode(val, &d1, &d2, 0);
if (d2>0) { LCDdigit(d2); } else { LCDout(1,' '); }
LCDdigit(d1);

LCDout(1, ':');
val = (ticks)%60;
BCDecode(val, &d1, &d2, 0);
LCDdigit(d2);
LCDdigit(d1);
}

LCDout_temp (unsigned short temp)
{
unsigned short val = temp;
char d1,d2,d3,d4;

BCDecode(val, &d1, &d2, &d3);
// d1 = val % 10;
// val = val / 10;
// d2 = val % 10;
// val = val / 10;
// d3 = val % 10;
if (d3 > 0) {
LCDdigit(d3);
}
else {
LCDout(1, ' ');
}
LCDdigit(d2);
LCDdigit(d1);
LCDout(1, 'F');
}

LCDout_volts (unsigned short volts)
{
unsigned short val = volts;
char d1,d2,d3,d4;

val = BCDecode(val, &d1, &d2, &d3);
// d1 = val % 10;
// val = val / 10;
// d2 = val % 10;
// val = val / 10;
// d3 = val % 10;
// val = val / 10;
d4 = val % 10;
if (d4 > 0) {
LCDdigit(d4);
}
else {
LCDout(1, ' ');
}
LCDdigit(d3);
LCDout(1, '.');
LCDdigit(d2);
LCDdigit(d1);
LCDout(1, 'v');
}

{
char d1,d2,d3,d4,d5;

val = BCDecode(val, &d1, &d2, &d3);
d4 = val % 10;
val = val / 10;
if (d4 > 0) {
d5 = val % 10;
LCDdigit(d5);
}
else {
LCDout(1, ' ');
}
LCDdigit(d4);
LCDdigit(d3);
LCDdigit(d2);
LCDdigit(d1);
}

LCDout_Mamps (unsigned short ma)
{
unsigned short val = ma/8;
char d1,d2,d3,d4;

val = BCDecode(val, &d1, &d2, &d3);
LCDdigit(val);
LCDdigit(d3);
LCDdigit(d2);
LCDdigit(d1);
LCDout(1, 'm');
LCDout(1, 'a');
LCDout(1, ' ');
}

LCDout_amps (unsigned short amps)
{
unsigned short val = amps;
char d1,d2,d3,d4;

val = BCDecode(val, &d1, &d2, &d3);
LCDdigit(d3);
LCDout(1, '.');
LCDdigit(d2);
LCDdigit(d1);
LCDout(1, 'a');
LCDout(1, ' ');
}

// Watchdog Timer interrupt service routine
// Runs each second
interrupt (WDT_VECTOR)
watchdog_timer(void)
{
unsigned short volts,temp;

seconds++;

_BIC_SR_IRQ(CPUOFF);             // Clear CPUOFF bits to wake up main loop

}

//
// so we stablize it by taking an average reading over 65,000 readings
// and return that value, and then use hysteresis to have it snap
// from one position to the next.
//
newDial()
{
unsigned short pos, ch1;

while (selecting < 2) {  // no button pressed
pos = ch1/64; //  ndiv;
if ((pos > dialpos) && (ch1-5 < lastdial))
{ pos = dialpos; }
if ((pos < dialpos) && (ch1+5 > lastdial))
{ pos = dialpos; }
if (pos != dialpos) {
// we have a new position, update globals and return
lastdial = ch1;
dialpos = pos;
selecting = 1;     // reset watchdog timeout shutdown
return dialpos;
}
}
return dialpos;
}

int
SelectInput(char *options)
{
LCDout(0, 0xC0); // position cursor at 2nd line
LCDouts(options);
LCDout(0, 0xC0); // position cursor at 2nd line
LCDout(0, 0x0F);  // On, Cursor, Blink

// init globals
dialpos = 16;
lastdial = 1060;

selecting = 1;
while (selecting < 2) {  // button not pressed (again)
LCDout(0, 0xC0+dialpos); // update cursor position
}
return dialpos;  // return position when button pressed.
}

int
buttonPress()
{
int val;
unsigned int ch1, ch2, pos, cpos;

while (1) {
//
// Offer selection of setting to edit
//
LCDout(0, 0x01);  // Clear Display
LCDout(0, 0xC0);  // Move Cursor to position for this charger
// LCDouts("X A:CtE B:CtE   ");
LCDouts("XS A23E23 B23E23");
// wait 1-2 sec to be sure button is fully back up
// sec2 = seconds;
// while (sec2 == seconds);
LCDout(0, 0xC0); // position cursor at 2nd line
LCDout(0, 0x0F);  // On, Cursor, Blink

dialpos = 16;
lastdial = 1060;
selecting = 1;

while (selecting < 2) {  // button not pressed (again)
LCDout(0, 0x80); // position cursor at 1st line, 3rd char "Px "
switch (pos) {
case 0:
{
break;
}
case 1:
case 2:
{
break;
}
case 3:
case 4:
case 5:
{
LCDouts("A: Charge ");
LCDdigit(pos-2);
break;
}
case 6:
case 7:
case 8:
{
LCDouts("A: Edit   ");
LCDdigit(pos-5);
break;
}
case 9:
{
break;
}
case 10:
case 11:
case 12:
{
LCDouts("B: Charge ");
LCDdigit(pos-9);
break;
}
case 13:
case 14:
case 15:
{
LCDouts("B: Edit   ");
LCDdigit(pos-12);
break;
}
}
LCDouts("    ");
LCDout(0, 0xC0+pos); // position cursor at 2nd line pos
}
//
// Execute Selected Action, and Return.
// For Editing, because of stack overflow problems,
// we return back to main to execute them.
//
switch (pos) {

case 0: return 0;
case 1:
case 2:  return 1;

case 3:
case 4:
case 5:
{
return 0;
}
case 6:
case 7:
case 8:
{
return pos-4;  // 2,3,4
}
case 9:  return 1;

case 10:
case 11:
case 12:
{
return 0;
}
case 13:
case 14:
case 15:
{
return pos-8;  // 5,6,7
}
}
// return if not editing settings
return 0;  // done
}
}

//
// Input Button interrupt occurs when button pressed
// Button is in two states, up or down.
// if it's up, we interrupt when it goes down,
// but don't change state unless the button stays down
// for a sufficient time, 30-50ms.
// If it was down, and goes back up,
// we acknowledge the button by incrementing the selecting variable.
//

interrupt (PORT1_VECTOR) p1int(void)
{
int in = P1IFG;
if (in & 0x1) {   // P1.0 changed hardware state
//
// If button state was up, wait for a while to confirm it is now down
//
if ((P1IES & 0x01) == 0x01) { // int on high-low transition, so button is up and went down
//
// Aclock is running at 2-Mhz.  60,000 ticks would be 30msec.
// we wait until it rolls through zero twice, that would be anywhere from 60k-120k
// while (TAR > 4) {
// in = P1IN;
// }
in = P1IN;
while (TAR > 4) {
in = P1IN;
}
// now check if button is still down, if so, change interrupt to fire
// when it goes up, and return from interrupt

if ((in & 0x01)==0x00) {  // button is still down
P1IES = 0x00; // now interrupt on button-up low-high transition
}
}
// we have low-high interrupt, so button was down, now it is up.
else {
// while (TAR > 4) {  // wait until TAR rolls to zero
// in = P1IN;
// }
in = P1IN;
while (TAR > 4) {
in = P1IN;
}
if ((in & 0x01)==0x01) {  // button is still up
P1IES = 0x01; // now interrupt on button-down high-low transition
// button is now considered pressed
if (selecting==0) {
selecting=1;
_BIC_SR_IRQ(CPUOFF);             // Clear CPUOFF bits to wake up main loop
}
else {
selecting++;  // increment to indicate button press
}
}
}
}
P1IFG = 0;  // clear handled interrupt
}

unsigned short tar;

//
// Timer_A Interrupt Vector (TAIV) handler for CCR0 and CCR0 capture/compare,
// and overflow interrupts
interrupt (TIMERA0_VECTOR) a0timer(void)
{
unsigned short v;
tar = TAR;
if (TAIV == 10) {
}
}

//
// TACH signal went low
// We get the rpm by measuring how long it was from
// the last event to this one for the last 4 events (2 revolutions)
// and generate a running average from them.
// If we are in startup with less than 4 events, just use the last one.
//
// To get rpm, we get the rev per second.  we have a 32k clock,
// so 32768 ticks per second, and thus 32768 / ticks = rev per second,
// so if we get 16384 ticks, it would be 2 revolutions in a second.
// We divide that in half since we have 2 events per revolution,
// so 16384 / ticks = rev per second, if we multiply by 60 seconds per minute,
// we get revolutions per minute (rpm).
// We should not see less than 100rpm during cranking, which is 10,000 ticks
// and thus times 4 = 40,000 which still fits into a 16bit unsigned short.

unsigned short tachTimes[4];

interrupt (PORT2_VECTOR) p2int(void)
{
unsigned short in = P2IFG;
unsigned short tar = TAR;
unsigned short tach0;
// if (in & 0x20) { }  // P2.5 changed hardware state
if (tar < lastTach) {  // timer rolled over
tach0 = tar + (0xFFFF - lastTach);  // get up to here, plus the roll
}
else {
tach0 = tar - lastTach;
}
lastTach = tar;
tach++;
if (tach > 3) {
tachTimes[ (tach & 0x3) ] = tach0;
// now compute average of last four values
tach0 = tachTimes[0] + tachTimes[1] + tachTimes[2] + tachTimes[3];
tach0 = tach0 >> 2;
// do injector spray after each ignition event once we've made at least one revolution
rpm = (32768 / tach0) * 3;  // this gives us a better number

startInjector();
// wake up main loop to update injectorPulseWidth after we have started
// nope, we must do it here, because if main is running the display (which takes 100ms)
// wake up will do nothing, so we must force it now from interrupt.
// we re-enable interrupts for pwm timers to execute.
// if we get another tach interrupt before we're done here, we are toast!

// _EINT();  // Now Enable interrupts
// updateInjectorWidth();
// updateThrottle ();
}
// rpm = (16384 / tach0) * 60;
rpm = (32768 / tach0) * 3;  // this gives us a better number
P2IFG = 0;       // clear handled interrupt
_BIC_SR_IRQ(CPUOFF);             // Clear CPUOFF bits to wake up main loop
return;
}

//
// spray the fuel injector.
// do a peak of full-on for 1.2 ms, then 25% pwm duty for remaining on-time
//
void startInjector() {
tar = TAR;
injectorStartTime = tar;   // get timer value of when to leave it off
CCR2 = tar + 39;   // 39 is 1.2ms for 32k timer
CCTL2 = OUT;  // turn ON injector
CCTL2 = CCIE | OUTMOD_5;  // turn off at CCR2 after 1.2ms, and interrupt
injectorIsOn = 1;         // peak period
injectorPulseWidth = injectorBaseWidth;
}

void updateInjectorWidth() {
//
// When we get tach input we wake up here
// to read the sensors and initiate the injector spray
//
// For our 4cyl 159ci tractor engine, and a 45lb/hr injector, we have a base injector time of 9ms for
// each ignition event.   It is adjusted by the o2sensor, airTemp, waterTemp, and MAP readings
//
// At our max of 2500 rpm = 41.666 RpS = 1/x = 24ms per revolution.
// With two events per rev, that leaves us 12ms, so we are almost 100% at 2500
// At 2200 rpm = 36.6 RpS = 1/x = 27ms per revolution, so we have 13.6ms

// We use the previous sensor values to initiate the injector pulse width,
// and then while that is going, we can do the 2-3ms needed to update the sensor readings
//
// read all the sensors, this takes
//
// read the MAP using 3v reference, 16x sample

// baseWidth is 9ms, which is 32*9 = 288 clock ticks
//
injectorBaseWidth = dial >> 1;  // divide in half,  0 to 512 range 0 to 16ms

//
// read the O2 sensor using 1.5v reference
//
// read the chip temperature as proxy for outside air temp  using 1.5v ref
//

//
// adjust pulse width for MAP and temps until water temp is at operating temp
// and then use o2 sensor for closed loop feedback
//
injectorPulseWidth = injectorBaseWidth;
if (waterTemp < 15) {  // doing open loop
//
}
else if (o2Sensor < 500) {  // o2sensor is lean, slightly richen until o2 goes rich
// injectorPulseWidth = injectorPulseWidth + 1;
}
else if (o2Sensor > 520) {
// injectorPulseWidth = injectorPulseWidth - 1;
}
}

unsigned short diff;
unsigned short throttleHold     =   40;
unsigned short throttleLowHold  =   64;
unsigned short throttleOpen     =   10;
unsigned short throttleClose    =  900;
unsigned short throttleLowClose = 1600;

void updateThrottle ()
{
//
// adjust throttle position to maintain rpm setting
//

throttleTimeOn = 8;  // constant

if (tps < targetTPS) {  // need to close it
diff = targetTPS - tps;
if (diff > 12) {
// set pw to slow close
// if tp is almost closed, we must use less force
if (diff > 200) {
throttleTimeOff = throttleLowClose;
}
else {
throttleTimeOff = throttleClose;
}
}
else {
throttleTimeOff = throttleHold;
if (tps > 800) {
throttleTimeOff = throttleLowHold;
}
}
}
else {
diff = tps - targetTPS;
if (diff > 12) {   // need to open it
if (diff > 200) {
throttleTimeOff = throttleOpen; // fast open
}
else if (tps > 800) {
throttleTimeOff = throttleOpen;
}
else {
throttleTimeOff = throttleOpen;
}
}
else {
throttleTimeOff = throttleHold;
}
}
}

//
// Timer_A Interrupt Vector (TAIV) handler for CCR1 and CCR2 capture/compare,
// and overflow interrupts
interrupt (TIMERA1_VECTOR) axtimer(void)
{
unsigned short v;
tar = TAR;
v = TAIV;
if (v == 2) {  // CCR1 compare
// if throttle was ON,
// then the output was turned off at compare,
// and we must setup to turn it back on at next compare
if (throttleIsOn) {
CCR1 = tar + throttleTimeOff;
// CCTL1 = 0;  // turn OFF throttle
CCTL1 = CCIE | OUTMOD_1;  // turn on at CCR1
throttleIsOn = 0;
}
else {
CCR1 = tar + throttleTimeOn;
// CCTL1 = OUT;  // turn ON throttle
CCTL1 = CCIE | OUTMOD_5;  // turn off at CCR1
throttleIsOn = 1;

}
}
else if (v == 4) { // CCR2 compare
// we initiate the full-on peak period,
// we in the interrupt we are doing the pwm hold
// use a on time of .125ms, offtime of .250ms
// until the injectorOffTime is reached, then we leave it off
if (injectorIsOn) {
// if the on period has been reached, the spray is complete and we leave it off
injectorIsOn = 0;
unsigned short onTime = (tar - injectorStartTime);
// turn back on if still not done with hold time.
if (onTime < injectorPulseWidth) {
CCR2 = tar + injectorTimeOff;
CCTL2 = CCIE | OUTMOD_1;  // turn on at CCR2
}
else {
// keep it off, done
CCR2 = 0;
CCTL2 = 0;
}
}
else {
CCR2 = tar + injectorTimeOn;
CCTL2 = OUT;  // turn ON injector
CCTL2 = CCIE | OUTMOD_5;  // turn off at CCR2
injectorIsOn = 1;
}
}
else {  // overflow, timer hit 0xffff

}
}

unsigned short tdel = 1;
unsigned short t1 = 0;
unsigned short t2 = 0;

//
// MAIN
//
int main(void)
{
int x;
unsigned short s;

WDTCTL = WDTPW + WDTHOLD;             // Stop WDT
DCOCTL = 0xE0;   // DCO=7, MOD=0
BCSCTL1 = 0x07;  // RSEL=7    == 5Mhz for F1232
// BCSCTL1 = (DIVA1 | DIVA0) | 0x07;  // Aclk/8 32k now 4k, RSEL=7,  4Mhz at 3v

// check if we have a working 32k ACLK
TACTL = TASSEL_ACLK + TACLR + MC_CONT;
delay(1200);  // wait
if (TAR == 0) {
Reboot(); // can't run without working 32k clock, wait for it to warm up
}
P1OUT = 0x00;                     // Clear
P2OUT = 0x00;                     // Clear

//
// copy from flash into memory sys config settings
// check for bootstrap uninitialized values
FConfig = (struct ConfigEFI *)0x1000;
Config = *FConfig;
if (Config.valid == 0xFF) {
Config.valid = 0;
SaveConfig();
Reboot(); // can't run without working 32k clock, wait for it to warm up
}

// ADC10ON and 64x sample and hold time.
// 2.5V reference
// ADC10ON and 16x sample and hold time.
//
// P2.0=A0
// P2.1=A1 O2
// P2.2=A2 Dial
// P2.3=A3 WaterTemp
// P2.4=A4 RPM Dial
// P3.6=A6 MAP
// P3.7=A7 TPS Throttle Position
//

// P2.5  tach input
// P1.0  board button  input
// P1.1  fuel pump relay output
// P1.2  throttle
// P1.3  fuel injector

P1DIR = 0x0E;   // output for P1.1,2,3, input for P1.0
P1OUT = 0x00;
P2DIR = 0x00;

P1IES = 0x00;  // init with high-low transition, so button must 1st go down
P1IE  = 0x01;  // enable interrupts on P1.0

P2IES = 0x00;  // init with high-low transition for TACH signal
P2IE  = 0x20;  // enable interrupts on P2.5

// PWM output for charge transistor control
TACTL = TASSEL_ACLK + TACLR + MC_STOP;
P1SEL = 0x0C;     // P1.3 is TimerA2 output for Throttle,  P1.2 is TimerA1 for Throttle

// P1.0 is Button input, P2.3 is select dial
initLCD();

// Turn on LCD Display
LCDout(0, 0x0C);  // Display On, no Cursor, no Blink
delay(750*20);
LCDout(0, 0x06);  // Move cursor to right
LCDout(0, 0x01);  // Clear Display
delay(750*20);

//
// initialize variables used by watchdog now before enable interrupts
//
tach = 0;
selecting = 0;
selectime = 0;
seconds = 0;

CCR1 = 0;
CCR2 = 0;
CCTL2 = 0x00; //init off
CCTL1 = 0x00; //init off
TACTL = TASSEL_ACLK + TACLR + TAIE + MC_CONT; // start timer

//
// see if button is down, if so we go into edit mode,
// otherwise, get ready to run the engine
//
if (1) { // ((P1IN & 0x1)==0) {  // p1.0 is down
LCDouts("   Nimble");
LCDout(0, 0xC0); // position cursor at char 8
LCDouts("Motorsports, LLC");
for(x=0; x<20; x++) {
delay(25000);
}
LCDout(0, 0x01);  // Clear Display
delay(750*20);
LCDouts("    Electronic      ");
LCDout(0, 0xC0); // position cursor at char 8
LCDouts(" Fuel Injection 1.0 ");
for(x=0; x<20; x++) {
delay(25000);
}
}

P1IFG = 0;  // clear button interrupt

//
// we are ready to run the engine, turn on the fuel pump
//
P1OUT = 0x02;

WDTCTL = WDT_ADLY_1000;               // Set Watchdog Timer interval back to 1000 (1-sec)
IE1 |= WDTIE;                         // Enable WDT interrupt
// _EINT();                              // Now Enable interrupts

// Init throttle PWM
// CCR0 = 0xFFFF;
// TACTL = TASSEL_SMCLK + TACLR + TAIE + MC_UPTO_CCR0; // start timer
TACTL = TASSEL_ACLK + TACLR + TAIE + MC_CONT; // start timer
//
// crack open the throttle for starting
//
throttleIsOn = 1;
throttleTimeOn = 640;
throttleTimeOff = 3200;
CCR1 = throttleTimeOn;
CCTL1 = OUT;  // turn ON throttle
CCTL1 = CCIE | OUTMOD_5;  // turn off at CCR1 and interrupt

injectorBaseWidth = 9*32;   // 9ms
injectorPulseWidth = injectorBaseWidth;
injectorTimeOff = 32;
injectorTimeOn = 32;
dstate = 0;

_EINT();  // Now Enable interrupts

while (1) {
selecting = 0;
//  Sleep until next interval status update
//_BIS_SR(CPUOFF);                 // Enter LPM3
//_NOP();

// we wake up when ignition event occurs,
// recalculate the injector on time

updateInjectorWidth();

t1 = TAR;

updateThrottle ();

t2 = TAR;

dstate++;
// do display in tiny steps
if (dstate == 1) {
LCDout(0, 0x80); // A position
}
else if (dstate == 2) {
//LCDouts("F:");
LCDouts("R:");
}
else if (dstate == 3) {
}
else if (dstate == 4) {
LCDouts(" I:");
}
else if (dstate == 5) {
}
else if (dstate == 6) {
LCDout(0, 0xC0);  // Move Cursor to position for this charger
}
else if (dstate == 7) {
LCDouts("T:");
}
else if (dstate == 8) {
}
else if (dstate == 9) {
LCDouts(" D:");
}
else if (dstate == 10) {
}
else {
dstate = 0;  // restart
}
t2 = TAR;

// see if we woke up from button press interrupt
if (selecting > 0) {
selectime = 0;
// because of stack overflow problem, we return from buttonPress
// and execute any editing choice here.
x = buttonPress();
if (x == 0) {
LCDout(0, 0x0C);  // On, no Cursor, no Blink
selecting = 0;
}
else if (x == 1) {
// sysSettings();
// SaveConfig(0);
}
else if (x < 5 ) { // edit settings, 2,3,4
// editSettings("A", x-2, &cset);
// SaveConfig();
}
else if (x < 8) { // edit settings   5,6,7
// editSettings("B", x-5, &cset);
// SaveConfig();
}
}
}

}
13.
Okay, I know I'm not going to be explaining it properly (10 shots of whisky isn't helping), but maybe this wall of text might make sense.

Hydraulic pumps are great, positive displacement, double-lip seals and thick packing helps leakage- measure the volume of fluid pumped and if you know the volume of the cylinder you can calculate position. I think you're trying to avoid pressure relief valves- that simplifies the plumbing and also prevents blowing out the cylinder packing.

You will encounter problems with trying to determine position from flow: no matter how well the pump and metering device are sealed, there will be leakage and possible back-flow when the pump is not running and the cylinder is under stress from compression (being pushed back) or extension (being pulled out). This will make your calculations drift over time unless you recalibrate periodically: run the hydraulic cylinder all the way in, zero the count, run it out and measure the flow volume to reach full extension. You could then, maybe, scale things accordingly and have reasonable accuracy calculating the cylinder's position, given you know how far the cylinder will travel for a particular volume of fluid pumped in or out.

To 'sort of' make my point:
Cranes do not rely on how many times a pump has turned to determine how far the crane boom has extended. They use something like what hvontres suggested- a known, fixed-length spool of cable that unwinds as the boom extends and retracts with the boom and uses a rotary encoder to determine position based on the spool's rotation and knowing that the cable is under no significant tension and won't stretch, which if it did stretch that would be analogous to your pump leaking or such).

Granted, that is an extreme case where the crane boom extension can be anywhere from a few meters to 100 meters or more, but I'm guessing that is probably far more than you need.

My opinion is that a linear position encoder mounted to the hydraulic cylinder would probably be your best bet.
14.
A DC motor (either brushed or brushless) will need some kind of feed back to control position. With a stepper, the encoder can be used for servo like control, or it can be used to detect missed steps after the motion is done. This way, the motor is run in open loop and if there were any missing steps during the move, you could command a couple of extra steps at the end. With a stepper, you are more likely to loose steps at higher speeds, since motor torque falls off pretty rapidly.

Having worked around servo systems for the last 18 years, I would not reccomend them for the uninitiated. Proper tuning and hookups can be a real pain without the propper tools. Also, the effects of feedback failures can be very spectacular and dangerous (imagine an axis flying towards you with full torque applied after the encoder fails and then hitting the end stops with a loud bang... that'll wake you up) But for the really adventurous, they do offer the potential for more performance. One of these days I should set up some articles on how a servo system works and can be tuned.... but that would be a bit too much like work
15.
In my mind a servo implies feedback.

I think it's best to use some sensor to ensure a hydraulic valve position to guard against Mis steps and glitches like you mentioned. Even in a stepper based system. The control will need to be able to tolerate the resolution of the step size though otherwise it will oscillate if it is trying to reach a position between two steps.

One other sensor type for linear position would be a lvdt transducer which is used in aerospace hydraulic actuators.
16.
I would suggest a shaft encoder, especially once the new Tiva Launch pads get out. That one will have TWO quadarature counters on board, so interfacing to the encoder should be very simple. As far as controlling the position of a Hydraulic cylinder, you might be better off getting your feedback from the rod itself. One way that I played with years ago while working on the animation system for a Rose Parade Float (http://www.csupomona.edu/~library/specialcollections/rosefloat/floatimages/1993photo.html) is to use a 10 turn pot with a torstion spring, a large wheel and some steel cable. As the rod extends, the pot turns and you get absolute position feedback from the cylinder. I think if you try to just measure the flow, you will integrate up large errors.

17.
A Ultrasonic car stopper project I made for my garage to inform as to how far I should pull my car forward. This project uses a MSP430 launchpad and a HSC - 04 ultrasonic range module as well as a RGB LED and a photosensitive resistor.

Schematic here: garage_msp430.pdf

Source here:
(i'll clean it up in abit, was having trouble with my debugger hence all the globals)
/*  * Author: Nathan Zimmerman  * Date: 6/22/13  *  * Description: A simple program to turn on a LED with the HC-SR04  * module to turn on a LED if the module detects a object closer  * than the trip point which is set by a pot  *  */ #include "msp430g2553.h" #include "stdint.h" //GPIO Pins #define trigger_pin           BIT2 #define echo_input_pin        BIT1 #define pot                   BIT0 #define bled                 BIT0 #define bled_setup            P2DIR #define bled_output        P2OUT #define bled_setup_on        bled_setup |= bled #define bled_on               bled_output |= bled #define bled_off            bled_output &=~bled #define rled                 BIT1 #define rled_setup            P2DIR #define rled_output        P2OUT #define rled_setup_on         rled_setup |= rled #define rled_on               rled_output |= rled #define rled_off            rled_output &=~rled #define gled                 BIT2 #define gled_setup            P2DIR #define gled_output           P2OUT #define gled_setup_on        gled_setup |= gled #define gled_on               gled_output |= gled #define gled_off             gled_output &=~gled //Ultrasound parameters #define timer_period        62500 // 4 * 62500 = 250ms #define trigger_pulse        (timer_period - 5) #define us_per_cm            58 #define time_to_trigger_us   450 #define distance_check        100 #define count_clk_divider    4 #define bad_measurement       40000 #define max_distance         400 //centimeters //Statics static uint16_t echo_pulse_counts = 0; static uint16_t distance_set_value =0; static uint16_t adc_val=0; static uint16_t distance=0; //Functions void clk_setup_1mhz(); void setup_trigger_pulse(); void setup_gpio_echo_interrupt(); void setup_adc_input(); uint16_t compare_distance_vs_threshold(); uint16_t get_adc_counts(); uint16_t get_distance_cm(); //Main void main(void) {     clk_setup_1mhz();     setup_trigger_pulse();     setup_gpio_echo_interrupt();     setup_adc_input();     bled_setup_on;     gled_setup_on;     rled_setup_on;     bled_off;     gled_off;     rled_off;     while(1)     {             distance = get_distance_cm();             if(distance)    //Check for out of range measurement             {                 if(distance<compare_distance_vs_threshold())                 {                     gled_off;                     rled_on;                 }                 else                 {                     gled_on;                     rled_off;                 }             }         }     } } // End of main void clk_setup_1mhz() {     BCSCTL1 = CALBC1_1MHZ;     DCOCTL = CALDCO_1MHZ;     WDTCTL = WDTPW + WDTHOLD; } void setup_trigger_pulse() {     P1DIR |= trigger_pin;     P1OUT &= ~trigger_pin;     P1SEL |= trigger_pin;     CCR0 = timer_period;     CCTL1 = OUTMOD_7;     CCR1 = trigger_pulse;     TACTL = TASSEL_2 + MC_1+ ID_2;     __enable_interrupt(); } void setup_gpio_echo_interrupt() {     P1DIR &= ~echo_input_pin;     P1OUT &= ~echo_input_pin;     P1IE |= echo_input_pin;     P1IES |= echo_input_pin;     P1IFG &= ~echo_input_pin; } void setup_adc_input() {     ADC10CTL1 |= CONSEQ1;     ADC10CTL0 |= ADC10SHT_2 + ADC10ON + MSC;     ADC10AE0 |= pot;     ADC10CTL0 |= ADC10SC + ENC; } uint16_t get_distance_cm() {     if(echo_pulse_counts>bad_measurement)         return 0;     else         return (echo_pulse_counts*count_clk_divider - (time_to_trigger_us))/ us_per_cm; } uint16_t get_adc_counts() {     return ADC10MEM; } uint16_t compare_distance_vs_threshold() {     uint32_t trip_threshold =0;     adc_val=0;     adc_val = get_adc_counts();     _delay_cycles(1);     trip_threshold = ((unsigned long)0x190*((unsigned long)adc_val))>>10;     _delay_cycles(1);     return (uint16_t)(trip_threshold & 0xFFFF); } #pragma vector=PORT1_VECTOR __interrupt void Port_1(void) {     echo_pulse_counts = TAR;     P1IFG &= ~echo_input_pin;     _delay_cycles(10); }
18. swampdonkeykami got a reaction from username in garden irrigation controller
Here's a trivial little controller that uses a Chinese soil moisture sensor, a relay and Adafruit solenoid valve to keep my wife's raised bed garden from dying.

My thinking was that a timer is a stupid way to water a garden. What if it rains all spring (like it has been)?

The MSP430's analog pin is used to monitor the sensor and then fires the relay to open the solenoid valve if the reading is too low. There's a 30 second delay in the void loop to keep the solenoid from floating...

http://arduinoforgoodnotevil.blogspot.ca/2013/04/forget-to-water-your-garden-no-problem.html

Unfortunately, after about a week the sensor cathode completely corroded!! I came home to a not so happy wife with a flooded garden! Ooops... it's on a timer until I make (and test) a stainless steel probe.

19.
This paper might be of interest, apropos capacitive moisture sensing. (Has RF communications also.)

Boston University College of Engineering, Department of Electrical and Computer Engineering WiGreen Soil Moisture Sensing System, MCL Technical Report: TR-05-02-2006 Kurt Matarese Gregg Fischer Naman Gupta Philip Kim  Prof. Thomas Little (Sponsor)   December 15, 2006   http://hulk.bu.edu/pubs/papers/2007/TR-05-02-2007.doc     Aside about resistive sensing - why measure so frequently?  (one comment talked about every minute).   Can't you get by with measuring maybe once every hour, or every few hours? Things I have seen about using two nails talk about replacing the electrodes once a year.   (Of course this probably depends on your weather and soil properties.)
20. swampdonkeykami reacted to spirilis in G2955 Game Pad
Ok so this is an idea I've had in my mind ever since the Arduino Esplora came out.  It's not an exact comparison since the G2955 doesn't have native USB (so you can't use this as a game controller necessarily) but it does have lots of pins and plenty of Flash + generous RAM to do the job IMO, also doesn't require anything super fancy like MSP430X support so the current stable release of mspgcc will work with it.

The idea is a MSP430G2955-based game pad with a standard 20-pin BoosterPack pinout in the middle for the LCD display.  Other LCD boosterpacks would have to be sourced to add the display (similar to the Arduino Esplora, although I don't know if the headers in the middle of that one is remotely compatible with the TI BoosterPack standard... the pin count is oddly similar though).

This allows easy expansion; sandwich other boosterpacks in between or maybe put them on the back-side, and have your LCD boosterpack of choice on top.  Easily upgrade your "game" by using a fancier color LCD or have a simple interactive monitoring system with a cheap B&W like the Nokia 1202.

This is the general layout I had in mind:

Features would include:
MSP430G2955 with 32.768KHz XTAL BoosterPack layout similar to RobG's G2955 LaunchPad board, with the exception of P2.6/P2.7 in the upper right being replaced with other pins so P2.6/2.7 can be dedicated to the XTAL
Port 1 dedicated to 8 HW-debounced pushbuttons; digital pad up/down/left/right, a "B" and "A" button on the right side, and "L" and "R" up top on the backside of the PCB.
6-pin FTDI header for UART work, should also be capable of powering the board by feeding the Vcc line into an LDO regulator.
USB connector for power only (feed into same LDO as the FTDI header).
Some sort of connector, JST or whatever, for connecting LiPoly or AA battery pack (I bought some sort of 2xAA battery pack from Seeed that has a JST-style connector)
Buzzer (connected to P4.1, Timer_B CCR1)
Microchip 23LC512 (64KB SPI SRAM) for auxiliary storage of sprites or whatnot.  64KB is still addressable with 16-bits so it should be reasonably code efficient to use.
WS2811 LED or 2 connected to P4.7 for miscellaneous use.
On/Off switch connected to P2.2, an interrupt-capable pin that can be used by firmware to "turn off" the unit (put all HW into sleep/powerdown mode and enter LPM4 or LPM3 if the gamepad also intends to keep a clock)
50-mil eZ430 header on the left side for easily plugging into an MSP430 LaunchPad and reprogramming.  Kit would include matching female & male 50-mil headers.  Will have to ponder location... might put it up on top instead so it's out of the way of your hands.

The general layout for the LaunchPad pins in the center would be like this:

(J1)
Vcc
P2.0 (A0)
P3.5 (UCA0RXD)
P3.4 (UCA0TXD)
P4.6 (A15, CA6)
P3.0 (UCB0STE, UCA0CLK, A5)
P3.3 (UCB0CLK, UCA0STE)
P4.5 (TB0.2, A14, CA5)
P4.4 (TB0.1, A13, CA4)
P4.3 (TB0.0, A12, CA3)

(J2)
GND
P3.6 (TA1.1, A6)
P2.1 (A1)
TEST
RESET
P3.1 (UCB0SIMO, UCB0SDA)
P3.2 (UCB0SOMI, UCB0SCL)
P2.4 (VREF+/VeREF+, TA0.2, A4)
P2.3 (VREF-/VeREF-, TA0.1, A3)
P3.7 (TA1.2, A7)

A lot of those pins aren't interrupt capable (only P1.x and P2.x are interrupt-capable), but this layout does follow TI's BYOB standard -- http://processors.wiki.ti.com/index.php/BYOB

Other functions as listed:
P4.0 - SPI SRAM HOLD
P4.2 - SPI SRAM ChipSelect
P2.2 - On/Off switch
P4.7 - WS2811 LEDs
P4.1 - Buzzer (TB0.1)
P2.5 - RTS input from FTDI header
P3.7 - General purpose 1206 SMD LED w/ 1K resistor inline (TA1.2) (could be used for power or on/off signal, simpler to interface than the WS2811's)

While the obvious use-case of this is as a game pad, it's also useful as a practical control interface for other analog or digital sensor projects that you can configure "in the field" using the UI buttons and LCD.

So whatcha think?
21.
You need to a capacitive sensor rather than a resistive one, otherwise it will always electroplate your sendor with crud. The capacitive ones change capacitance dependind on how much water is around them.

You might be able to use a 555 timer to do the capacitance based pulsing in hardware the more water, the higher the capacitance, tge slower the 555 wil cycle
22.
Here's my 2 cents: since the probe is symmetrical, you could bind both pins to analog input enabled pins, then once every minute or so you set one pin to digital high and measure the other pin using analog read. The next minute you switch the roles of the two pins. This way you don't have the probe powered all the time, plus you spread the load over the two pins. Also, most PCB fabs offer ENIG gold plating for your circuits, this would make the probe live virtually forever.
//pseudo code bool forwardMeasure; void setup() { set up timer to fire every 30 seconds or so attach interrupt routine "Measure" to the timer pinMode(Probe1, INPUT); pinMode(Probe2, INPUT); LPM3; // Go to sleep, the timer will wake us up twice a minute } void loop() { // loop should not be executed at all LPM3; // if it does, go to sleep } void Measure() { int A, B; forwardMeasure = !forwardMeasure; // flip the forwardMeasure boolean if (forwardMeasure) { A = Probe1; B = Probe2; } else { B = Probe1; A = Probe2; } digitalWrite(A, HIGH); analogRead(; // Do this first to enable the ADC pin delay(20); // wait a little to measure the probe int x = analogRead(; // then actually measure pinMode(Probe1, INPUT); // Set the probe pins to floating again pinMode(Probe2, INPUT); // Set the probe pins to floating again if (x < WET) { digitalWrite(Solenoid, HIGH); } else { digitalWrite(Solenoid, LOW); } }
23. swampdonkeykami got a reaction from yyrkoon in garden irrigation controller
Here's a trivial little controller that uses a Chinese soil moisture sensor, a relay and Adafruit solenoid valve to keep my wife's raised bed garden from dying.

My thinking was that a timer is a stupid way to water a garden. What if it rains all spring (like it has been)?

The MSP430's analog pin is used to monitor the sensor and then fires the relay to open the solenoid valve if the reading is too low. There's a 30 second delay in the void loop to keep the solenoid from floating...

http://arduinoforgoodnotevil.blogspot.ca/2013/04/forget-to-water-your-garden-no-problem.html

Unfortunately, after about a week the sensor cathode completely corroded!! I came home to a not so happy wife with a flooded garden! Ooops... it's on a timer until I make (and test) a stainless steel probe.

24.
if you want that to fire off 5 times though it should be more like
while(retry >= 5) or while(retry > 6)
25.
Something like this . . .
while(retry > 5){    digitalWrite(forwardPin, LOW);    //pin action delay here    digitalWrite(reversePin, LOW);    // pin action delay here        retry++; }
×
• Blog

• #### Activity

×
• Create New...