Electric remote controlled Tiger 1 Tank toy + arduino
March 15, 2010 1 Comment
It all started when I found this ex-display toy in a store, for “only” 30£ (rather than the initial 90£), because it was missing its remote…
Wow, something cheap and “not working” is the perfect excuse to open and hack it straight away…
- RX5_ATS305R Pin FUNCTIONS
- RX5_ATS305R Pin OUTS
Look at the 2 motors and the nice black gearbox… this tank must be at least 3-5 times more powerful than my previous “miniature” one !
Luckily it uses the TX5 ATS305T micro controller for the remote control, which turns out to be quite easily hackable too, as per the excerpts from the specifications.
1st Step : remote controlled mode
Here’s the code simply listening for IR commands and transforming them into commands for the motors
#define P_LEFT_FWD 5
#define P_RIGHT_FWD 6
#define P_LEFT_BCK 7
#define P_TURRET_L 9
#define P_TURRET_R 10
#define P_UP 2
#define P_FIRE 3
unsigned int mPrevSpeed = 9999;
int mPrevDir = 9999;
#define MIN_SPEED 50
void setup(){
ir_setup();
pinMode(P_LEFT_FWD, OUTPUT);
pinMode(P_LEFT_BCK, OUTPUT);
pinMode(P_RIGHT_FWD, OUTPUT);
pinMode(P_TURRET_L, OUTPUT);
pinMode(P_TURRET_R, OUTPUT);
pinMode(P_UP, OUTPUT);
pinMode(P_FIRE, OUTPUT);
}
void loop(){
while(! ir_receiveCommands(5000)){
}
unsigned int speed = ir_getSpeed();
int dir = ir_getDir();
if((speed != mPrevSpeed) || (dir != mPrevDir)){
mPrevSpeed = speed;
mPrevDir = dir;
updateSpeed();
}
}
void updateSpeed(){
motors(mPrevSpeed + mPrevDir, mPrevSpeed - mPrevDir);
}
void motors(int speedL, int speedR){
analogWrite(P_LEFT_FWD, validateSpeed(speedL));
analogWrite(P_RIGHT_FWD, validateSpeed(speedR));
}
byte validateSpeed(int speed){
if(speed <= 0) return 0;
int result = speed + MIN_SPEED;
if(result >= 255) result = 254;
return result;
}
void motorsStop(){
digitalWrite(P_LEFT_FWD, LOW);
digitalWrite(P_RIGHT_FWD, LOW);
digitalWrite(P_LEFT_BCK, LOW);
}
void turret(int duration){
if(duration < 0){
analogWrite(P_TURRET_L, 100);
}else{
analogWrite(P_TURRET_R, 100);
}
delay(abs(duration));
// stop everything
digitalWrite(P_TURRET_L, LOW);
digitalWrite(P_TURRET_R, LOW);
}
void upDown(){
digitalWrite(P_UP, HIGH);
delay(2000);
digitalWrite(P_UP, LOW);
}
void fire(){
analogWrite(P_FIRE, 75);
delay(1000);
digitalWrite(P_FIRE, LOW);
}
<pre>
<pre>
And here’s some updated code for the Syma s026 RC Helicopter infra red remote control.
// More RELIABLE version using TIMERS directly, rathen than pulseIn()
// PWM on PIN 9 and 10 will be DISABLED
const byte IRpin = 12;
const byte SIGNAL_SIZE = 29;
const byte SIGNAL_SPEED = 7;
const byte SIGNAL_DIR = 6;
const unsigned int PULSE_START = 1600;
const unsigned int PULSE_0_MIN = 400;
const unsigned int PULSE_0_MAX = 600;
const unsigned int PULSE_1_MIN = 800;
const unsigned int PULSE_1_MAX = 1000;
const byte LEFT_RIGHT_MIDDLE = 64;
unsigned int mSpeed;
unsigned int mDir;
unsigned int tempLastPulse;
int tempSpeed;
int tempDir;
int tempBit;
void ir_setup(){
pinMode(IRpin, INPUT);
setTimingConf();
}
unsigned int ir_getSpeed(){
return mSpeed;
}
int ir_getDir(){
return mDir - LEFT_RIGHT_MIDDLE;
}
boolean ir_receiveCommands(unsigned long timeout){
unsigned long startTime = micros();
while(getLowPulse(IRpin, timeout) < PULSE_START){
if(micros() - startTime > timeout) return false;
}
tempSpeed = 0;
for(int i=SIGNAL_SPEED; i>0; i--){
tempBit = getBit(getLowPulse(IRpin, timeout));
if(tempBit < 0) return false;
if(tempBit == 1) tempSpeed += bit(i);
}
tempDir = 0;
for(int i=SIGNAL_DIR; i>0; i--){
tempBit = getBit(getLowPulse(IRpin, timeout));
if(tempBit < 0) return false;
if(tempBit == 1) tempDir += bit(i);
}
mSpeed = tempSpeed;
mDir = tempDir;
return true;
}
int getBit(unsigned int pulse){
if(pulse > PULSE_0_MIN && pulse < PULSE_0_MAX) return 0;
if(pulse > PULSE_1_MIN && pulse < PULSE_1_MAX) return 1;
return -1;
}
//int asInt(byte start, byte end){
// int tempResult = 0;
// for(int i=start; i<end; i++){
// int bit = getBit(mPulses[i]);
// if(bit < 0) return -1;
// if(bit == 1) tempResult += bit(end - i - 1);
// }
//
// return tempResult;
//}
/* TIMING STUFF*/
int getLowPulse(byte pin, unsigned int timeoutMicroSecs){
unsigned int timeout = timeoutMicroSecs / 4;
TCNT1 = 0; // timer reset
// wait until it gets LOW or timesout
while(digitalRead(pin) == HIGH && TCNT1 < timeout) {}
// time out
if(TCNT1 >= timeout) return -1;
// start conting the pulse
TCNT1 = 0; // timer reset
while(digitalRead(pin) == LOW && TCNT1 < timeout) {}
// pulse too long
if(TCNT1 >= timeout) return -2;
return TCNT1 * 4; // counts in 4microSecs increments
}
void setTimingConf(){
TCCR1A = 0x00; // COM1A1=0, COM1A0=0 =Disconnect Pin OC1 from Timer/Counter 1 -- PWM11=0,PWM10=0 = PWM Operation disabled
// ICNC1=0 = Capture Noise Canceler disabled -- ICES1=0 =Input Capture Edge Select (not used) -- CTC1=0 =Clear Timer/Counter 1 on Compare/Match
// CS12=0 CS11=1 CS10=1 =Set prescaler to clock/64
TCCR1B = 0x03; // 16MHz clock with prescaler means TCNT1 increments every 4uS
// ICIE1=0 = Timer/Counter 1, Input Capture Interrupt Enable -- OCIE1A=0 =Output Compare A Match Interrupt Enable -- OCIE1B=0 =Output Compare B Match Interrupt Enable
// TOIE1=0 =Timer 1 Overflow Interrupt Enable
TIMSK1 = 0x00;
}
<pre>
<pre>
2nd Step : programmed in C# (.NET Micro Framework) on the FEZ Domino board
Program.cs
using System;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using GHIElectronics.NETMF.FEZ;
using GHIElectronics.NETMF.Hardware;
namespace ToyTank
{
class CONSTANTS
{
public const FEZ_Pin.AnalogIn SENSOR_IR_PIN = FEZ_Pin.AnalogIn.An0;
public const FEZ_Pin.Interrupt SENSOR_US_PIN = FEZ_Pin.Interrupt.Di1;
public const FEZ_Pin.PWM TRACK_LEFT_PIN = FEZ_Pin.PWM.Di6;
public const FEZ_Pin.PWM TRACK_RIGHT_PIN = FEZ_Pin.PWM.Di5;
public const FEZ_Pin.Digital TRACK_RIGHT_BACK_PIN = FEZ_Pin.Digital.Di7;
public const FEZ_Pin.Interrupt IR_CMD_PIN = FEZ_Pin.Interrupt.Di13;
public const FEZ_Pin.Digital FIRE_SENSOR = FEZ_Pin.Digital.Di11;
public const FEZ_Pin.Digital FIRE_MOTOR = FEZ_Pin.Digital.Di10;
public const FEZ_Pin.PWM TURRET_LEFT_PIN = FEZ_Pin.PWM.Di8;
public const FEZ_Pin.PWM TURRET_RIGHT_PIN = FEZ_Pin.PWM.Di9;
public const FEZ_Pin.Interrupt IR_BEACON_WIDE_PIN = FEZ_Pin.Interrupt.Di3;
public const FEZ_Pin.Interrupt IR_BEACON_NARROW_PIN = FEZ_Pin.Interrupt.Di2;
}
class Program
{
Distance _distance = new Distance();
Tank _tank = new Tank();
IR_CMD_Syma_s026 _irCmd;
bool _autoPilot = false;
IRBeacon _wideBeacon;
IRBeacon _narrowBeacon;
Program()
{
_irCmd = new IR_CMD_Syma_s026(CONSTANTS.IR_CMD_PIN, IRCmdReceived);
//_wideBeacon = new IRBeacon(CONSTANTS.IR_BEACON_WIDE_PIN, IRWideBeaconCodeReceived);
//_narrowBeacon = new IRBeacon(CONSTANTS.IR_BEACON_NARROW_PIN, IRNarrowBeaconCodeReceived);
//_tank.Turret(-50);
}
void IRWideBeaconCodeReceived(int code)
{
Debug.Print("Wide Beacon Code: " + code);
}
void IRNarrowBeaconCodeReceived(int code)
{
Debug.Print("Narrow Beacon Code: " + code);
_tank.Turret(0);
}
void IRCmdReceived(int speed, int dir, int vertical, bool leftButton, bool rightButton, int trimmer)
{
Debug.Print(speed + " - " + dir + " - " + vertical + " - " + leftButton + " / " + rightButton + " - " + trimmer);
// update AutoPilot mode
if (leftButton)
{
_autoPilot = !_autoPilot;
Debug.Print("AutoPilot: " + _autoPilot);
}
if (! _autoPilot)
{
_tank.Tracks(speed - dir, speed + dir);
_tank.Turret(vertical * 7);
if (rightButton) _tank.Fire();
}
}
void AutoPilot()
{
while (true)
{
if (_autoPilot)
{
int speedPerc = _distance.Read_cm();
if (speedPerc <= 30)
{
_tank.Tracks(0, 0);
Thread.Sleep(2000);
_tank.Turret(50);
Thread.Sleep(2000);
_tank.Turret(-50);
Thread.Sleep(2000);
_tank.Turret(0);
Thread.Sleep(2000);
_tank.Fire();
Thread.Sleep(2000);
_tank.Tracks(0, -1); //full turn right
while (_distance.Read_cm() < 50) Thread.Sleep(30);
}
else
{
if (speedPerc > 30 && speedPerc < 50)
{
int delta = (50 - speedPerc) * 3;
_tank.Tracks(speedPerc - delta, speedPerc + delta);
}
else
{
_tank.Tracks(speedPerc, speedPerc);
}
}
}
Thread.Sleep(100);
}
}
//Main ENTRY POINT into the program !
public static void Main()
{
try
{
new Program().AutoPilot();
//new Program();
Thread.Sleep(Timeout.Infinite);
}
catch (Exception e)
{
Debug.Print(e.StackTrace);
}
}
} // End Class Program
}
Tank.cs
using System;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using GHIElectronics.NETMF.FEZ;
using GHIElectronics.NETMF.Hardware;
namespace ToyTank
{
class Tank
{
PWM _trackLeft = new PWM((PWM.Pin)CONSTANTS.TRACK_LEFT_PIN);
PWM _trackRight = new PWM((PWM.Pin)CONSTANTS.TRACK_RIGHT_PIN);
OutputPort _trackRightBack = new OutputPort((Cpu.Pin)CONSTANTS.TRACK_RIGHT_BACK_PIN, false);
PWM _turretLeft = new PWM((PWM.Pin)CONSTANTS.TURRET_LEFT_PIN);
PWM _turretRight = new PWM((PWM.Pin)CONSTANTS.TURRET_RIGHT_PIN);
// at the end of fire, the switch is activated and takes that pin to GROUND
InputPort _fireSensor = new InputPort((Cpu.Pin)CONSTANTS.FIRE_SENSOR, true, Port.ResistorMode.PullDown);
OutputPort _fireMotor = new OutputPort((Cpu.Pin)CONSTANTS.FIRE_MOTOR, false);
public Tank()
{
_trackLeft.Set(false);
_trackRight.Set(false); // MANDATORY, BEFORE we set it backwards otherwise we'll burn the transistors !
_trackRightBack.Write(false);
_turretLeft.Set(false);
_turretRight.Set(false);
_fireMotor.Write(false);
}
public void Turret(int perc)
{
bool left = perc < 0;
perc = System.Math.Abs(perc);
if (perc > 100) perc = 100;
if (left)
{
_turretRight.Set(false);
_turretLeft.Set(1000, (byte)perc);
}
else
{
_turretLeft.Set(false);
_turretRight.Set(1000, (byte)perc);
}
}
public void Tracks(int leftPerc, int rightPerc)
{
if (leftPerc < 0) leftPerc = 0;
if (rightPerc < 0) rightPerc = 0;
if (leftPerc > 100) leftPerc = 100;
if (rightPerc > 100) rightPerc = 100;
_trackLeft.Set(1000, (byte)leftPerc);
_trackRight.Set(1000, (byte)rightPerc);
}
public void Fire()
{
new Thread(FireThread).Start();
}
void FireThread()
{
_fireMotor.Write(true); //start the motor
Debug.Print("FIRED " + _fireSensor.Read());
while (! _fireSensor.Read()) ; //wait until the switch is pressed
Thread.Sleep(300);
_fireMotor.Write(false); //stop the motor
Debug.Print("STOP FIRED ");
}
}
class Distance
{
FEZ_Components.SharpIRDistanceSensor _IRSensor = new FEZ_Components.SharpIRDistanceSensor(CONSTANTS.SENSOR_IR_PIN, FEZ_Components.SharpIRDistanceSensor.SharpSensorType.GP2D120);
FEZ_Components.SRF05USDistanceSensor _USSensor = new FEZ_Components.SRF05USDistanceSensor(CONSTANTS.SENSOR_US_PIN);
public int Read_cm()
{
// use only the Ultrasound for now, as more accurate...
int dist = _USSensor.GetDistance_cm(580000); // max 1000 cm
for (int i = 0; i < 5 && dist < 0; i++) dist = _USSensor.GetDistance_cm(580000); //RE-TRY max 5 times
if (dist < 0) dist = 1000;
return dist;
}
}
}
IR_Cmd.cs
using System;
using System.Threading;
using System.Collections;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using GHIElectronics.NETMF.FEZ;
using GHIElectronics.NETMF.Hardware;
namespace ToyTank
{
/*
* Ranges are:
* 0 to 220
* -62 to 62
* -14 to 14
* 2 to 60
*/
public delegate void IRCmdDelegate(int speed, int dir, int vertical, bool leftButton, bool rightButton, int trimmer);
class IR_CMD_Syma_s026 : IDisposable
{
const byte SIGNAL_SIZE = 26; // MAX size is 29 !
public const byte SIGNAL_SPEED = 7;//from 0 to here
public const byte SIGNAL_DIR = 13;//from end of SignalSpeed to here
public const byte SIGNAL_BUTTONS = 17;//from end of SignalDir to here
public const byte SIGNAL_VERTICAL = 21;//from end of SignalButtons to here
public const byte SIGNAL_TRIMMER = 26;//from end of SignalVertical to here
const int PULSE_START_MIN = 1650;
const int PULSE_START_MAX = 1750;
const int PULSE_0_MIN = 450;
const int PULSE_0_MAX = 600;
const int PULSE_1_MIN = 850;
const int PULSE_1_MAX = 1000;
const byte SPEED_OFFSET = 80;
const byte LEFT_RIGHT_MIDDLE = 64;
const byte VERTICAL_MIDDLE = 16;
const byte VALUE_LEFT_BUTTON = 22;
const byte VALUE_RIGHT_BUTTON = 26;
private readonly InterruptPort _ir;
private readonly IRCmdDelegate _callback;
public IR_CMD_Syma_s026(FEZ_Pin.Interrupt ir_pin, IRCmdDelegate callbackWhenCmdReceived)
{
_ir = new InterruptPort((Cpu.Pin)ir_pin, false, Port.ResistorMode.Disabled, Port.InterruptMode.InterruptEdgeBoth);
_ir.OnInterrupt += new NativeEventHandler(IR_OnInterrupt);
_callback = callbackWhenCmdReceived;
// start the executor thread. ONLY 1 thread, rather than creating and killing them...
new Thread(CallbackExecutor).Start();
}
public void Dispose()
{
_ir.Dispose();
}
private long _startPulseTicks = -1;
private int _pulseCount = -1;
private long[] _pulses = new long[SIGNAL_SIZE];
private long[] _prevPulses = new long[SIGNAL_SIZE];
private bool _interruptContinue = true;
private void IR_OnInterrupt(uint port, uint state, DateTime time)
{
// if not -999, then the executor thread hasn't yet dealt with the previous value, there's no point in providing a new one...
lock (_prevPulses) _interruptContinue = (_prevPulses[0] == -999);
if (!_interruptContinue) return;
if (state == 0)
{// interested in LOW pulses
_startPulseTicks = time.Ticks;
}
else if(_startPulseTicks > 0)
{// if high AND a pulse is started
long pulse = (time.Ticks - _startPulseTicks) / 10; // from ticks to microSeconds
_startPulseTicks = -1; // will wait for the next start
if (pulse > PULSE_START_MIN && pulse < PULSE_START_MAX)
{//start counting pulses
_pulseCount = 0;
}
else if (_pulseCount >= 0)
{//continue counting pulses
_pulses[_pulseCount] = pulse;
_pulseCount++;
// FINAL, we've read what we wanted let our creator know !
if (_pulseCount == SIGNAL_SIZE)
{
// clone the original array as other interrupts might come and need the original array
// needs to lock as the Executor thread accesses this too...
lock(_prevPulses) _prevPulses = (long[])_pulses.Clone();
// end of this command, so reInit so that we wait for another start
_pulseCount = -1;
}
}
}
}
// Simply monitor _prevPulses for changes and call the calback. Will implicitly ignore intermediary changes to pulses,
// there's no need to check for new pulses until the previous callback method has finished.
private void CallbackExecutor()
{
bool callback = false;
byte[] bits, prevBits = null;
while (true)
{
callback = false;
bits = null;
// needs lock as the interrupt thread accesses this too...
lock(_prevPulses)
{
// user first element as flag (if _prevPulses[0] == -999 -> nothing has been received)
if (_prevPulses[0] != -999)
{
bits = GetBits(_prevPulses);
_prevPulses[0] = -999;//mark that we've read these pulses, the interrupt can go on put new ones
}
}
// do the callback ONLY if the new information is different...
if (!DeepEquals(bits, prevBits)) callback = true;
// if bits == null = we had a bad pulse, we'll have to start from scratch
if (callback && bits != null)
{
new Thread(delegate { NewFullSignalReceived(bits); }).Start();
//NewFullSignalReceived(bits);
prevBits = (byte[])bits.Clone();
}
Thread.Sleep(10);
}
}
private void NewFullSignalReceived(byte[] bits)
{
//Debug.Print(toStr(bits));
int speed = 0, dir = 0, buttons = 0, vertical = 0, trimmer = 0;
for (int i = 0; i < bits.Length; i++)
{
if (bits[i] == 1)
{
if (i < SIGNAL_SPEED) speed += (1 << (SIGNAL_SPEED - i));
else if (i < SIGNAL_DIR) dir += (1 << SIGNAL_DIR - i);
else if (i < SIGNAL_BUTTONS) buttons += (1 << SIGNAL_BUTTONS - i);
else if (i < SIGNAL_VERTICAL) vertical += (1 << SIGNAL_VERTICAL - i);
else if (i < SIGNAL_TRIMMER) trimmer += (1 << SIGNAL_TRIMMER - i);
}
}
speed = speed - SPEED_OFFSET;
if (speed < 0) speed = 0;
_callback(speed, dir - LEFT_RIGHT_MIDDLE, vertical - VERTICAL_MIDDLE, buttons == VALUE_LEFT_BUTTON, buttons == VALUE_RIGHT_BUTTON, trimmer);
}
private static String toStr(Object arg)
{
if (arg == null || !arg.GetType().IsArray) throw new ArgumentException("Need to receive array");
Array array = arg as Array;
String result = "[";
foreach (Object elem in array) result += elem + ", ";
result = result.Substring(0, result.Length - 2) + "]";
return result;
}
private static byte[] GetBits(long[] pulses)
{
byte[] result = new byte[pulses.Length];
for (int i = 0; i < pulses.Length; i++)
{
int bit = GetBit(pulses[i]);
if (bit < 0) return null;
result[i] = (byte)bit;
}
return result;
}
private static int GetBit(long pulse)
{
//Debug.Print(pulse.ToString());
if(pulse > PULSE_0_MIN && pulse < PULSE_0_MAX) return 0;
if(pulse > PULSE_1_MIN && pulse < PULSE_1_MAX) return 1;
return -1;
}
private static bool DeepEquals(byte[] a1, byte[] a2)
{
if (a1 == null) return a2 == null;
if (a2 == null) return a1 == null;
if (a1.Length != a2.Length) return false;
for (int i = 0; i < a1.Length; i++)
{
if (a1[i] != a2[i]) return false;
}
return true;
}
} // END IR_Syma_s026
}
IR_Beacon.cs
using System;
using System.Threading;
using System.Collections;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using GHIElectronics.NETMF.FEZ;
using GHIElectronics.NETMF.Hardware;
namespace ToyTank
{
public delegate void IRBeaconDelegate(int code);
class IRBeacon : IDisposable
{
const byte SIGNAL_SIZE = 8;
const int T = 560; // in microseconds
const int DELTA = 300; // in microseconds
const int T_MIN = T - DELTA;
const int T_MAX = T + DELTA;
const int T3_MIN = 3*T_MIN;
const int T3_MAX = 3*T_MAX;
const int START_MIN = 8*T_MIN;
const int START_MAX = 8*T_MAX;
private readonly InterruptPort _ir;
private readonly IRBeaconDelegate _callback;
public IRBeacon(FEZ_Pin.Interrupt ir_pin, IRBeaconDelegate callbackWhenCodeReceived)
{
_ir = new InterruptPort((Cpu.Pin)ir_pin, false, Port.ResistorMode.Disabled, Port.InterruptMode.InterruptEdgeBoth);
_ir.OnInterrupt += new NativeEventHandler(IR_OnInterrupt);
_callback = callbackWhenCodeReceived;
// start the executor thread. ONLY 1 thread, rather than creating and killing them...
new Thread(CallbackExecutor).Start();
}
public void Dispose()
{
_ir.Dispose();
}
private long _startPulseTicks = -1;
private int _pulseCount = -1;
private long[] _pulses = new long[SIGNAL_SIZE];
private long[] _prevPulses = new long[SIGNAL_SIZE];
private bool _interruptContinue = true;
private void IR_OnInterrupt(uint port, uint state, DateTime time)
{
// if not -999, then the executor thread hasn't yet dealt with the previous value, there's no point in providing a new one...
lock (_prevPulses) _interruptContinue = (_prevPulses[0] == -999);
if (! _interruptContinue) return;
if (state == 1)
{// interested in HIGH pulses
_startPulseTicks = time.Ticks;
}
else if (_startPulseTicks > 0)
{// if high AND a pulse is started
long pulse = (time.Ticks - _startPulseTicks) / 10; // from ticks to microSeconds
_startPulseTicks = -1; // will wait for the next start
if (pulse > START_MIN && pulse < START_MAX)
{//start counting pulses
_pulseCount = 0;
}
else if (_pulseCount >= 0)
{//continue counting pulses
_pulses[_pulseCount] = pulse;
_pulseCount++;
// FINAL, we've read what we wanted let our creator know !
if (_pulseCount == SIGNAL_SIZE)
{
lock (_prevPulses) _prevPulses = (long[])_pulses.Clone();
// end of this command, so reInit so that we wait for another start
_pulseCount = -1;
}
}
}
}
// Simply monitor _prevPulses for valid data and call the calback. Will implicitly ignore intermediary changes to pulses,
// there's no need to check for new pulses until the previous callback method has finished.
private void CallbackExecutor()
{
int code = -2;
while (true)
{
// needs lock as the interrupt thread accesses this too...
lock (_prevPulses)
{
// user first element as flag (if _prevPulses[0] == -999 -> nothing has been received)
if (_prevPulses[0] != -999)
{
code = GetCode(_prevPulses);
_prevPulses[0] = -999;//mark that we've read these pulses, the interrupt can go on put new ones
}
}
if (code > 0) _callback(code); // ignore invalid codes = errors or interference
Thread.Sleep(300);//go slowly, it's not a high priority this one...
}
}
private int GetCode(long[] pulses)
{
int code = 0;
for (int i = 0; i < pulses.Length; i++ )
{
int bit = GetBit(pulses[i]);
if (bit < 0) return -1;
if (bit == 1) code += 1 << i;
}
return code;
}
private static int GetBit(long pulseSize)
{
if ((pulseSize > T_MIN) && (pulseSize < T_MAX)) return 0;
if ((pulseSize > T3_MIN) && (pulseSize < T3_MAX)) return 1;
return -1;
}
} // End class IRBeacon
}
DistanceDetector.cs
using System;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using GHIElectronics.NETMF.Hardware;
using GHIElectronics.NETMF.FEZ;
namespace GHIElectronics.NETMF.FEZ
{
public static partial class FEZ_Components
{
public class SharpIRDistanceSensor : IDisposable
{
private AnalogIn _irInput;
float Y0 = 0;
float X0 = 0;
float Y1 = 0;
float X1 = 0;
float C = 0;
public enum SharpSensorType : byte
{
GP2Y0A21YK = 0,
GP2D120 = 1,
}
public void Dispose()
{
_irInput.Dispose();
}
public SharpIRDistanceSensor(FEZ_Pin.AnalogIn pin, SharpSensorType type)
{
_irInput = new AnalogIn((AnalogIn.Pin)pin);
_irInput.SetLinearScale(0, 330);
switch (type)
{
case SharpSensorType.GP2Y0A21YK:
Y0 = 10;
X0 = 315;
Y1 = 80;
X1 = 30;
break;
case SharpSensorType.GP2D120:
Y0 = 3;
X0 = 260;
Y1 = 30;
X1 = 45;
break;
}
C = (Y1 - Y0) / (1 / X1 - 1 / X0);
}
private float ReadDistance()
{
return C / ((float)_irInput.Read() + (float).001) - (C / X0) + Y0;
}
public int GetDistance_cm()
{
int result = (int)((ReadDistance() + ReadDistance() + ReadDistance()) / 3);
if (result > 50) result = -2; // too far to be accurate
else if (result < 3) result = -1; // too close to be accurate
return result; // _irInput.Read();
}
}//SharpIRDistanceSensor
public class SRF05USDistanceSensor : IDisposable
{
private TristatePort _usInput; // needs to be interrupt capable
public SRF05USDistanceSensor(FEZ_Pin.Interrupt pin)
{
_usInput = new TristatePort((Cpu.Pin)pin, true, false, Port.ResistorMode.Disabled);
}
public void Dispose()
{
_usInput.Dispose();
}
// need these methods to avoid throwing an exception
private void MakePortOutput()
{
if (_usInput.Active == false) _usInput.Active = true;
}
// need these methods to avoid throwing an exception
private void MakePortInput()
{
if (_usInput.Active == true) _usInput.Active = false;
}
public int GetDistance_cm(int timeoutTicks)
{
// 1. send a TRIGGER high 10microS pulse
MakePortOutput();
_usInput.Write(false);
Sleep_us(2);
_usInput.Write(true);
Sleep_us(10);
_usInput.Write(false);
// 2. wait for the response
// The speed of sound is 340 m/s or 29 microseconds per centimeter.
// The ping travels out and back, so to find the distance of the object we take half of the distance travelled.
MakePortInput();
long start = DateTime.Now.Ticks;
while (!_usInput.Read() && DateTime.Now.Ticks - start < timeoutTicks) ; // wait until it's HIGH or timeout
if (DateTime.Now.Ticks - start >= timeoutTicks) return -1;
start = DateTime.Now.Ticks;
while (_usInput.Read() && DateTime.Now.Ticks - start < timeoutTicks) ; // count the HIGH pulse OR timeout
if (DateTime.Now.Ticks - start >= timeoutTicks) return -2;
return (int)(DateTime.Now.Ticks - start) / 580; // divide bt 2x29 for us to cm and by 10 for tick to us
}
private static void Sleep_us(int microSecs)
{
long end = DateTime.Now.Ticks + microSecs * 10;
while (DateTime.Now.Ticks < end) ;
}
}
}
}












Pingback: Tiger 1 BB airsoft RC Tank « Robotics / Electronics / Physical Computing