Nov 102013
 

[EDIT] Here is a link to an update of these effects made by Stefan Mayer designed to work with the OctoWS2811 LED Library. Thanks a lot Stefan, great work!

[EDIT] I’m planning to clean up the Android control app source and put it on github, but in the meantime here’s the code.

[EDIT] If you end up using this code for a project please send me a link/pics/video! I love seeing what people do with this! If you like I’ll post a link to your project on the FastSPI FX – User Coolness post. Also make sure to thank Daniel Garcia (the genius dev of the FastSPI library) on the G+ FastSPI Community.

Here is an update to the Arduino FastSPI2 FX demo code (v0.51). I’m also introducing an Android Bluetooth Control App and a Chrome Extension.


I’ll continue to work on improving these programs but I think I have them to a point where it might be useful to some people- so here they are!

Here is the Arduino Sketch (v0.51)

Here is the Android App on the Google Play Store (v0.1a)

Here is the Chrome LED Controller Extension (v0.1)

— — — SETUP / NOTES — — —

ARDUINO:
– This version uses the Software Serial library and FastSPI2 library. Also the default serial speed is now 57600.
– The following is a breadboard layout and schematic for setting up a JY-MCU Bluetooth chip with an Arduino (Duemilenove or Nano) to drive a strip of addressable LEDs. This should work with any LED strip that is supported by FastSPI2 RC4 and later.
ArduinoBTLEDSimple_011_breadboard_bbArduinoBTLEDSimple_007_schematic_schem

ANDROID APP:
– To control LED strips via the Android app you’ll have to pair the JY-MCU module with your smartphone/tablet in the Bluetooth/Wireless settings. The PIN for these modules is usually 1234.

CHROME EXTENSION:
– To load the extension, unrar the file to a directory. Then in Chrome go to Settings>Extensions>Load Unpacked Extension. Then select the directory. Then you can launch it from there.
– This extension has been tested on Ubuntu 12.04 and WindowsXP. For Windows you’ll have to install the USB Serial drivers, but the automatic driver search seems to work fine for this.

— — —

If you find this really useful, please consider donating a little to the cause. Thanks!



I’m also grateful for bitcoin donations to: 1K5Yy77ejes2FZrHBG5fns3QAicnwZcduq

 

Oct 032013
 

[EDIT] Please check out the newest version of the FX Code (v0.51) with Android Bluetooth Control App and Chrome Control Extension.

[EDIT] Fixed a really dumb problem in the Arduino code, added a color picker to the python GUI, and uploaded a new video demo. The problem was I was using if (serial.available()) and should have been using while. That gummed up the works and made the LEDs flicker if you moved the sliders too fast. That’s fixed now. Should have waited and tested better but I was all excited to get this posted- live and learn, or more likely just repeat the same mistakes and correct myself as I go along.

Here’s another revision to the FastSPI2 effects demo code. Some code cleanups, a few new effects, and a python GUI (gtk). GUI can select effect by name and control brightness, delay, color-step, hue, and saturation of various effects.

Screenshot from 2013-10-03 19:52:46

Here’s the Arduino Code

Here’s the Python Code

 

If you find this really useful, please consider donating a little to the cause. Thanks!



I’m also grateful for bitcoin donations to: 1K5Yy77ejes2FZrHBG5fns3QAicnwZcduq

Aug 262012
 


This is UgBot: The ugliest electronic mess since Superman III.

UgBot technically isn’t a robot, just a remote controlled thing. It started as a test for my next robot project (to be called Dominar Rygel XVI)

I just had to work through a few simple ideas before I started on a more sophisticated platform and this is what I ended up with.

UgBot’s hardware is:
(1) Arduino Nano
(1) Bluetooth TTL Module
(2) Micro Servos (modified to continuous rotation)
(1) Breadboard (sawed in half)
(1) Battery Pack (4)AAA
Poweraide and Coke Caps for wheels
Lots of jumpers

and the software is:
-Arduino sketch with CmdMessenger, Servo, and SoftwareSerial libraries (software serial is so I don’t have to pull the TX/RX pins for debugging. It’s a big help)
-Android app which I basically stole from this guy Eirik Taylor’s project. I changed the UI and the serial commands but the rest is pretty much his code. I’m so lazy I didn’t even change the name so the app is still called BluCar from his project.

I absolve myself of the theft since he admits that he took most of the code from the BlueTooth Chat example in the Android SDK.

Here’s the Android and Arduino Code

Here’s updated android code (w/ multitouch enabled) Code

Sep 122000
 

I’m posting this request for help here so I can just post a link and not have to clutter up everybody’s forum.

So here’s the dealio –

I need to control multiple sliders (seekbars) at the same time to send bluetooth serial commands to another device.

I created one version that worked to send the serial commands, but wasn’t multitouch.

I’ve found several examples of how to setup a multi-touch capable view but now I cannot find where to handle click events when the views are combined in this way.

Here is a stripped down version of what I have so far.

I just need to understand how to create a function that will execute when I hit a button or change a value on the seek bar. If I could just change the value of the textView I think I could figure the rest out.

Any help or direction would be appreciated.

 
~MTouch.java

package funkboxing.mtouch;

import funkboxing.mtouch.R;
import android.os.Bundle;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.SeekBar;
import android.widget.TextView;

public class MTouch extends MultiTouchActivity {
	/** Called when the activity is first created. */

	private Button btn1;
	private Button btn2;
	private SeekBar seekbar1;
	private SeekBar seekbar2;
	private TextView tview1;
	private TextView tview2;
	private TextView tview3;
	private TextView tview4;	

	@Override
	public void onCreate(final Bundle savedInstanceState) {
		requestWindowFeature(Window.FEATURE_NO_TITLE);
		getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
		getWindow().clearFlags(
				WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);

		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		
		btn1 = (Button) findViewById(R.id.button1);
		btn1.setOnTouchListener(this);
		
		btn2 = (Button) findViewById(R.id.button2);
		btn2.setOnTouchListener(this);

		seekbar1 = (SeekBar) findViewById(R.id.seekbar1);
		seekbar1.setOnTouchListener(this);
		addMoveOutsideEnabledViews(seekbar1);
		
		seekbar2 = (SeekBar) findViewById(R.id.seekbar2);
		seekbar2.setOnTouchListener(this);
		addMoveOutsideEnabledViews(seekbar2);
		
		tview1 = (TextView) findViewById(R.id.textView1);
		tview2 = (TextView) findViewById(R.id.textView2);
		tview3 = (TextView) findViewById(R.id.textView3);
		tview4 = (TextView) findViewById(R.id.textView4);
		
	}
}

~MultiTouchActivity.java


package funkboxing.mtouch;

import java.util.ArrayList;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.FrameLayout;

public class MultiTouchActivity extends Activity implements OnTouchListener {

	private View parent;

	private final ArrayList[] recentTouchedViewsIndex = new ArrayList[10];

	private final ArrayList[] downTouchedViewsIndex = new ArrayList[10];

	private final ArrayList moveOutsideEnabledViews = new ArrayList();

	private int mTouchSlop = 24;

	public void addMoveOutsideEnabledViews(final View view) {
		moveOutsideEnabledViews.add(view);
	}

	private void dealEvent(final int actionPointerIndex,
			final MotionEvent event, final View eventView,
			final int actionResolved) {
		int rawX, rawY;
		final int location[] = { 0, 0 };
		eventView.getLocationOnScreen(location);
		// Log.v("tag", location + "");
		rawX = (int) event.getX(actionPointerIndex) + location[0];
		rawY = (int) event.getY(actionPointerIndex) + location[1];

		final int actionPointerID = event.getPointerId(actionPointerIndex);
		ArrayList hoverViews = getTouchedViews(rawX, rawY);

		if (actionResolved == MotionEvent.ACTION_DOWN) {
			downTouchedViewsIndex[actionPointerID] = (ArrayList) hoverViews
					.clone();
		}
		// deletes all views which where not clicked on ActionDown
		if (downTouchedViewsIndex[actionPointerID] != null) {
			final ArrayList tempViews = (ArrayList) hoverViews
					.clone();
			tempViews.removeAll(downTouchedViewsIndex[actionPointerID]);
			hoverViews.removeAll(tempViews);
		}

		if (recentTouchedViewsIndex[actionPointerID] != null) {
			final ArrayList recentTouchedViews = recentTouchedViewsIndex[actionPointerID];

			final ArrayList shouldTouchViews = (ArrayList) hoverViews
					.clone();
			if (!shouldTouchViews.containsAll(recentTouchedViews)) {
				shouldTouchViews.removeAll(recentTouchedViews);
				shouldTouchViews.addAll(recentTouchedViews);

				final ArrayList outsideTouchedViews = (ArrayList) shouldTouchViews
						.clone();
				outsideTouchedViews.removeAll(hoverViews);
			}

			recentTouchedViewsIndex[actionPointerID] = hoverViews;
			hoverViews = shouldTouchViews;
		} else {
			recentTouchedViewsIndex[actionPointerID] = hoverViews;
		}

		if (actionResolved == MotionEvent.ACTION_UP) {
			recentTouchedViewsIndex[actionPointerID] = null;
			downTouchedViewsIndex[actionPointerID] = null;
		}

		dumpEvent(event);
		for (final View view : hoverViews) {
			int x, y;
			view.getLocationOnScreen(location);
			x = rawX - location[0];
			y = rawY - location[1];

			// View does not recognize that the Pointer is
			// outside if the Pointer is not far away (>mTouchSlop)
			if (recentTouchedViewsIndex[actionPointerID] != null) {
				if (pointInView(x, y, mTouchSlop, view.getWidth(),
						view.getHeight())) {
					// Log.v("tag", "added because < mTouchSlop");

					if (!recentTouchedViewsIndex[actionPointerID]
							.contains(view)) {
						recentTouchedViewsIndex[actionPointerID].add(view);
					}
				} else if (moveOutsideEnabledViews.contains(view)) {
					Log.v("tag", "outside but gets event");
					recentTouchedViewsIndex[actionPointerID].add(view);
				}
			}
			final MotionEvent me = MotionEvent.obtain(event.getDownTime(),
					event.getEventTime(), actionResolved, x, y,
					event.getPressure(actionPointerIndex),
					event.getPressure(actionPointerIndex),
					event.getMetaState(), event.getXPrecision(),
					event.getYPrecision(), event.getDeviceId(),
					event.getEdgeFlags());
			me.setLocation(x, y);

			if (!me.equals(event)) {
				// deals the Event
				view.onTouchEvent(me);
			}

			// debug
			if (actionResolved == MotionEvent.ACTION_MOVE) {
				Log.v("tag",
						"#" + actionPointerIndex + " Rawx:" + rawX + " rawy:"
								+ rawY + " x:" + x + " y:" + y + " "
								+ view.toString());
			}
		}

	}

	private void dumpEvent(final MotionEvent event) {
		final String names[] = { "DOWN", "UP", "MOVE", "CANCEL", "OUTSIDE",
				"POINTER_DOWN", "POINTER_UP", "7?", "8?", "9?" };
		final StringBuilder sb = new StringBuilder();
		final int action = event.getAction();
		final int actionCode = action & MotionEvent.ACTION_MASK;
		sb.append("event ACTION_").append(names[actionCode]);
		if (actionCode == MotionEvent.ACTION_POINTER_DOWN
				|| actionCode == MotionEvent.ACTION_POINTER_UP) {
			sb.append("(pid ").append(
					action >> MotionEvent.ACTION_POINTER_ID_SHIFT);
			sb.append(")");
		}
		sb.append("[");
		for (int i = 0; i < event.getPointerCount(); i++) {
			sb.append("#").append(i);
			sb.append("(pid ").append(event.getPointerId(i));
			sb.append(")=").append((int) event.getX(i));
			sb.append(",").append((int) event.getY(i));
			if (i + 1 < event.getPointerCount()) {
				sb.append(";");
			}
		}
		sb.append("]");
		Log.d("tag", sb.toString());
	}

	private ArrayList getChildViews(final View view) {
		final ArrayList views = new ArrayList();
		if (view instanceof ViewGroup) {
			final ViewGroup v = ((ViewGroup) view);
			if (v.getChildCount() > 0) {
				for (int i = 0; i < v.getChildCount(); i++) {
					views.add(v.getChildAt(i));
				}

			}
		}
		return views;
	}

	private ArrayList getTouchedViews(final int x, final int y) {

		final ArrayList touchedViews = new ArrayList();
		final ArrayList possibleViews = new ArrayList();

		if (parent instanceof ViewGroup) {
			possibleViews.add(parent);
			for (int i = 0; i < possibleViews.size(); i++) {
				final View view = possibleViews.get(i);

				final int location[] = { 0, 0 };
				view.getLocationOnScreen(location);

				if (((view.getHeight() + location[1] >= y)
						& (view.getWidth() + location[0] >= x)
						& (view.getLeft() <= x) & (view.getTop() <= y))
						|| view instanceof FrameLayout) {
					touchedViews.add(view);
					possibleViews.addAll(getChildViews(view));
				}

			}
		}

		return touchedViews;

	}

	@Override
	public void onCreate(final Bundle instance) {
		super.onCreate(instance);
		requestWindowFeature(Window.FEATURE_NO_TITLE);
		getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
		getWindow().clearFlags(
				WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
		parent = findViewById(android.R.id.content).getRootView();
		parent.setOnTouchListener(this);
		mTouchSlop = ViewConfiguration.get(getApplicationContext())
				.getScaledTouchSlop();
	}

	@Override
	public boolean onTouch(final View v, final MotionEvent event) {

		// index of the pointer which starts this Event
		final int actionPointerIndex = event.getActionIndex();

		// resolve the action as a basic type (up, down or move)
		int actionResolved = event.getAction() & MotionEvent.ACTION_MASK;
		if (actionResolved < 7 && actionResolved > 4) {
			actionResolved = actionResolved - 5;
		}

		if (actionResolved == MotionEvent.ACTION_MOVE) {
			for (int ptrIndex = 0; ptrIndex < event.getPointerCount(); ptrIndex++) {
				// only one event for all move events.
				dealEvent(ptrIndex, event, v, actionResolved);
				Log.v("tag", "move" + ptrIndex);
			}

		} else {
			dealEvent(actionPointerIndex, event, v, actionResolved);
		}

		return true;
	}

	private boolean pointInView(final float localX, final float localY,
			final float slop, final float width, final float height) {
		return localX >= -slop && localY >= -slop && localX < ((width) + slop)
				&& localY < ((height) + slop);
	}
}


~main.xml