Hardware components | ||||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 3 | ||||
| × | 9 | ||||
Software apps and online services | ||||||
| ||||||
Hand tools and fabrication machines | ||||||
|
The key fob on my 2002 Ford Fairlane broke and was no longer able to be used. After dismantling the fob, it appeared that just the packaging and one of the switches was broken. I connected the switches (using a transistor for each switch) to be driven by a Particle Electron.
/* App to replace 2002 Ford Fairlane Key Fob
*/
void setup() {
pinMode(D2, OUTPUT);
pinMode(D3, OUTPUT);
pinMode(D4, OUTPUT);
Particle.function("lock", lock);
Particle.function("unlock", unlock);
Particle.function("boot", boot);
}
int lock(String command) {
digitalWrite(D3, HIGH);
delay(100);
digitalWrite(D3, LOW);
digitalWrite(D7, HIGH);
delay(500);
digitalWrite(D7, LOW);
return 1;
}
int unlock(String command) {
digitalWrite(D4, HIGH);
delay(100);
digitalWrite(D4, LOW);
delay(100);
digitalWrite(D4, HIGH);
delay(100);
digitalWrite(D4, LOW);
digitalWrite(D7, HIGH);
delay(500);
digitalWrite(D7, LOW);
return 1;
}
int boot(String command) {
digitalWrite(D2, HIGH);
delay(100);
digitalWrite(D2, LOW);
digitalWrite(D7, HIGH);
delay(500);
digitalWrite(D7, LOW);
return 1;
}
void loop() {
}
import android.content.Context;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import io.particle.android.sdk.devicesetup.ParticleDeviceSetupLibrary;
public class MainActivity extends AppCompatActivity {
public static String function;
private static Context mContext;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mContext = getApplicationContext();
ParticleDeviceSetupLibrary.init(this.getApplicationContext(), MainActivity.class);
// Declare and assign our buttons and text
Button lockButton = (Button) findViewById(R.id.lockButton);
View.OnClickListener lock = new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(getApplicationContext(), "Locking...", Toast.LENGTH_LONG).show();
function = "lock";
HelperThread thread = new HelperThread();
thread.setName("HelperThread");
thread.start();
}
};
lockButton.setOnClickListener(lock);
// Declare and assign our buttons and text
Button unlockButton = (Button) findViewById(R.id.unlockButton);
View.OnClickListener unlock = new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(getApplicationContext(), "Unlocking...", Toast.LENGTH_LONG).show();
function = "unlock";
HelperThread thread = new HelperThread();
thread.setName("HelperThread");
thread.start();
}
};
unlockButton.setOnClickListener(unlock);
// Declare and assign our buttons and text
Button bootButton = (Button) findViewById(R.id.bootButton);
View.OnClickListener boot = new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(getApplicationContext(), "Booting...", Toast.LENGTH_LONG).show();
function = "boot";
HelperThread thread = new HelperThread();
thread.setName("HelperThread");
thread.start();
}
};
bootButton.setOnClickListener(boot);
}
public void showConfirmation(int i, String function) {
switch(i) {
case 0:
if (function == "lock"){
Toast.makeText(MainActivity.mContext, "Unable to lock.", Toast.LENGTH_LONG).show();
} else if (function == "unlock") {
Toast.makeText(MainActivity.mContext, "Unable to unlock.", Toast.LENGTH_LONG).show();
} else if (function == "boot") {
Toast.makeText(MainActivity.mContext, "Unable to open boot.", Toast.LENGTH_LONG).show();
} else {
Log.e("Error", "Error");
}
break;
case 1:
if (function == "lock"){
Toast.makeText(MainActivity.mContext, "Locked.", Toast.LENGTH_LONG).show();
} else if (function == "unlock") {
Toast.makeText(MainActivity.mContext, "Unlocked.", Toast.LENGTH_LONG).show();
} else if (function == "boot") {
Toast.makeText(MainActivity.mContext, "Boot Opened.", Toast.LENGTH_LONG).show();
} else {
Log.e("Error", "Error");
}
break;
default:
Toast.makeText(MainActivity.mContext, "Unknown outcome", Toast.LENGTH_LONG).show();
}
}
}
Helper Thread
JavaThis handles the asynchronous function POST method for communicating with the Particle cloud service.
You will need to enter the device name or ID number of your own device.
You will need to enter the device name or ID number of your own device.
import android.support.annotation.NonNull;
import android.util.Log;
import java.io.IOException;
import io.particle.android.sdk.cloud.ParticleCloudException;
import io.particle.android.sdk.cloud.ParticleCloudSDK;
import io.particle.android.sdk.cloud.ParticleDevice;
import io.particle.android.sdk.utils.Async;
public class HelperThread extends Thread {
public static int resultCode;
public static int resultCode2;
public void run() {
ParticleDevice myDevice = null;
resultCode = 0;
resultCode2 = 0;
try {
myDevice = ParticleCloudSDK.getCloud().getDevice("YOUR DEVICE HERE");
getValue(myDevice, MainActivity.function);
} catch (ParticleCloudException | IOException | ParticleDevice.VariableDoesNotExistException e) {
e.printStackTrace();
}
}
public int getValue(ParticleDevice myDevice, final String function) throws ParticleCloudException, IOException, ParticleDevice.VariableDoesNotExistException {
Async.executeAsync(myDevice, new Async.ApiWork<ParticleDevice, Integer>() {
public Integer callApi(@NonNull ParticleDevice myDevice)
throws ParticleCloudException, IOException {
try {
resultCode = myDevice.callFunction(function);
} catch (ParticleDevice.FunctionDoesNotExistException e) {
e.printStackTrace();
}
return resultCode;
}
@Override
public void onSuccess(@NonNull Integer s) {
MainActivity confirmation = new MainActivity();
confirmation.showConfirmation(resultCode, function);
}
@Override
public void onFailure(@NonNull ParticleCloudException e) {
Log.e("Result", "Something went wrong making an SDK call: ", e);
}
});
return resultCode;
}
}
Layout file for the buttons
XMLUse this file to organise your on-screen buttons for the Android app. Wrapping the layout in a FrameLayout prevents the app from freezing during operation (allows it to keep receiving commands).
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/activity_main"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
>
<Button android:id="@+id/lockButton"
android:text="Lock"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_gravity="center_horizontal"
android:textSize="@dimen/textSize"
style="@style/Widget.AppCompat.Button.Colored"/>
<Button android:id="@+id/unlockButton"
android:text="Unlock"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_gravity="center_horizontal"
android:textSize="@dimen/textSize"
style="@style/Widget.AppCompat.Button.Borderless.Colored"/>
<Button android:id="@+id/bootButton"
android:text="Boot"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_gravity="center_horizontal"
android:textSize="@dimen/textSize"
style="@style/Widget.AppCompat.Button.Colored"/>
</LinearLayout>
</FrameLayout>
String constants file
XMLEnter OAuth information using: https://docs.particle.io/guide/how-to-build-a-product/authentication/#oauth and https://docs.particle.io/reference/android/#oauth-client-configuration
<string name="oauth_client_id">(client ID string goes here)</string>
<string name="oauth_client_secret">(client secret 40-char hex string goes here)</string>
Comments