Monday, June 20, 2011

optimistic about maven android integration

I've been using the maven-android-plugin recently. Here's a little video showing building, deploy, and undeploy of an simple application to my phone. I particularly love the android:deploy and android:undeploy goals.



To get the full benefit of these features in Eclipse we need m2e integration. Luckily, the m2e-android project is making good progress.

Good stuff.

Tuesday, June 14, 2011

windows dexdump failure

This is frustrating because it should be such a trivial thing. I don't know what the fix is. I was hopeful that the binary attached to Android Issue 14746 would fix me up but that didn't work out.

simple dx / dexdump on Mac


In Windows, I was getting a silly-stupid message from dx and then an error from dexdump so I wanted to try a dx / dexdump on Mac for a comparison. I picked a random JAR file.


[jjohnson@jjohnson 20110613]$ cp ~/.m2/repository/commons-lang/commons-lang/2.4/commons-lang-2.4.jar .
[jjohnson@jjohnson 20110613]$ dx --dex --output=test.apk commons-lang-2.4.jar
[jjohnson@jjohnson 20110613]$ dexdump -d -l xml test.apk

<api>
<package name="org.apache.commons.lang"
>
<class name="ArrayUtils"
extends="java.lang.Object"
abstract="false"
static="false"
final="false"
visibility="public"
>
...


Good enough. Easy peasy.

simple made difficult




C:\Documents and Settings\Jeremiah Johnson\20110613>copy ..\.m2\repository\commons-lang\commons-lang\2.4\commons-lang-2.4.jar .
C:\Documents and Settings\Jeremiah Johnson\20110613>dx --dex --output=test.apk commons-lang-2.4.jar

UNEXPECTED TOP-LEVEL EXCEPTION:
java.io.FileNotFoundException: commons-lang-2.4.jar (The system cannot find the file specified)
at java.util.zip.ZipFile.open(Native Method)
at java.util.zip.ZipFile.(ZipFile.java:127)
at java.util.zip.ZipFile.(ZipFile.java:144)
at com.android.dx.cf.direct.ClassPathOpener.processArchive(ClassPathOpener.java:205)
at com.android.dx.cf.direct.ClassPathOpener.processOne(ClassPathOpener.java:130)
at com.android.dx.cf.direct.ClassPathOpener.process(ClassPathOpener.java:108)
at com.android.dx.command.dexer.Main.processOne(Main.java:313)
at com.android.dx.command.dexer.Main.processAllFiles(Main.java:233)
at com.android.dx.command.dexer.Main.run(Main.java:185)
at com.android.dx.command.dexer.Main.main(Main.java:166)
at com.android.dx.command.Main.main(Main.java:90)
1 error; aborting

C:\Documents and Settings\Jeremiah Johnson\20110613>dx --dex --output="C:\Documents and Settings\Jeremiah Johnson\20110613\test.apk" "C:\Documents and Settings\Jeremiah Johnson\20110613\commons-lang-2.4.jar"
and was unexpected at this time.


and was unexpected at this time.


That's for real - did you see that in the previous output? That's the whole error message. Neat. So you can't use relative paths and you can't use an absolute path with a space? Perhaps Windows users already know what to really do but I'm just trying to be a good citizen and test a defect fix on Windows without tons of recent Windows experience.

final dexdump failure on windows




C:\Documents and Settings\Jeremiah Johnson\20110613>cd ..
C:\Documents and Settings\Jeremiah Johnson>move 20110613 C:\
C:\Documents and Settings\Jeremiah Johnson>cd C:\20110613
C:\20110613>dx --dex --output=C:\20110613\test.apk C:\20110613\commons-lang-2.4.
jar
C:\20110613>dexdump -d -l xml test.apk
E/dalvikvm( 3152): sysMapFileSegmentInShmem not implemented.
W/dalvikvm( 3152): Zip: cd map failed
Unable to open 'test.apk' as zip archive
Not Zip, retrying as DEX
E/dalvikvm( 3152): ERROR: Bad magic number (0x50 4b 03 04)
ERROR: Failed structural verification of 'test.apk'

C:\20110613>dexdump -d -l xml C:\20110613\test.apk
E/dalvikvm( 1740): sysMapFileSegmentInShmem not implemented.
W/dalvikvm( 1740): Zip: cd map failed
Unable to open 'C:\20110613\test.apk' as zip archive
Not Zip, retrying as DEX
E/dalvikvm( 1740): ERROR: Bad magic number (0x50 4b 03 04)
ERROR: Failed structural verification of 'C:\20110613\test.apk'

Sunday, June 05, 2011

jsp pre-compile and proper servlet classloading

Following my previous post, I configured JSP pre-compiling and adjusted my simple WebAppContext a bit to get the servlet classloader working properly. Without fixing the classloader, I couldn't actually use the pre-compiled JSPs. After getting the classloading worked out, I switched from WebAppContext to WebAppProvider: 1) WebAppProvider allows polling for redeploy of the WAR and 2) the WebAppContext is deprecated so I felt like I should use non-deprecated APIs in this new code.

The i-Jetty project is a pretty interesting project but goes a different route than what I'm doing. I followed some of the ideas there but simplified for what I need.

added configuration to WebAppContext



// start the Web server for config and maintenance
webServer = new Server(8080);
try {
WebAppContext webapp = new WebAppContext();
webapp.setConfigurationClasses(new String[] { DexWebInfConfiguration.class.getName(),
JettyWebXmlConfiguration.class.getName(), WebXmlConfiguration.class.getName() });
webapp.setContextPath("/");
webapp.setTempDirectory(new File("/sdcard/TGP-work/"));
webapp.setWar("/sdcard/TGP-webapps/TGP-ROOT.war");
webServer.setHandler(webapp);
}
catch (Exception e) {
Log.d(LOG_TAG, "unexpected exception setting temp: " + e);
}


DexWebInfConfiguration.java


When I started this class, I thought that there would be more to it so it is a regular class. I'll probably make it an inner class if it doesn't get any more complex that this. Right now, this is all that is needed to use the dex version of the servlets in my WAR.


/**
* Adds dex support to the classloader used in the Jetty WebInfConfiguration.
*
* <code>webapp.setConfigurationClasses(new String[] { DexWebInfConfiguration.class.getName() });</code>
*/
public class DexWebInfConfiguration extends WebInfConfiguration {
public void preConfigure(WebAppContext context) throws Exception {
context.setClassLoader(new DexClassLoader(context.getTempDirectory().getCanonicalPath()
+ "/webapp/WEB-INF/lib/classes.zip", context.getTempDirectory().getCanonicalPath(), null, getClass()
.getClassLoader()));
super.preConfigure(context);
}
}


the WAR file


I'm using a simple Maven project within Eclipse for my WAR file. I'm using the standard structure and added the following to the POM. The first chunk pre-compiles the JSPs and then the second chunk runs dex on all of the compiled code in the WAR (i.e. the JSPs as well as the regular servlets).

The first chunk was pretty must straight out of the jetty-jspc-maven-plugin docs and the second from i-Jetty.

<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty-jspc-maven-plugin</artifactId>
<version>7.3.0.v20110203</version>
<executions>
<execution>
<id>jspc</id>
<goals>
<goal>jspc</goal>
</goals>
<configuration>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<!-- use web.xml from jspc rather than src version -->
<webXml>${project.build.directory}/web.xml</webXml>
</configuration>
</plugin>

<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.1</version>
<executions>
<execution>
<id>generate-dex</id>
<phase>process-classes</phase>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<executable>${android.home}/platform-tools/dx</executable>
<arguments>
<argument>--dex</argument>
<argument>--verbose</argument>
<argument>--core-library</argument>
<argument>--output=${project.build.directory}/classes.dex</argument>
<argument>--positions=lines</argument>
<argument>${project.build.directory}/classes/</argument>
</arguments>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<id>copydex</id>
<phase>process-classes</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<tasks>
<mkdir
dir="${project.build.directory}/${project.artifactId}-${project.version}/WEB-INF/lib" />
<jar basedir="${project.build.directory}" update="true"
includes="classes.dex"
destfile="${project.build.directory}/${project.artifactId}-${project.version}/WEB-INF/lib/classes.zip" />
</tasks>
</configuration>
</execution>
</executions>
</plugin>


switch to WebAppProvider


I thought that I'd just need to change the WebAppContext stuff but I ended up also having to adjust the configuration as well. Temp directories are handled differently in the provider stuff and I don't totally understand it. Here's where I'm at with the code that sets up the server and then the configuration.

MyApp.class onCreate


// start the Web server for config and maintenance
webServer = new Server(8080);
try {
HandlerCollection handlers = new HandlerCollection();
ContextHandlerCollection contexts = new ContextHandlerCollection();
handlers.setHandlers(new Handler[] { contexts, new DefaultHandler() });
webServer.setHandler(handlers);

DeploymentManager deployer = new DeploymentManager();
deployer.setContexts(contexts);
webServer.addBean(deployer);

WebAppProvider webAppProvider = new WebAppProvider();
webAppProvider.setConfigurationClasses(new String[] { DexWebInfConfiguration.class.getName(),
JettyWebXmlConfiguration.class.getName(), WebXmlConfiguration.class.getName() });
webAppProvider.setExtractWars(true);
webAppProvider.setScanInterval(10);
webAppProvider.setMonitoredDirName("/sdcard/jetty/webapps/");
deployer.addAppProvider(webAppProvider);
}
catch (Exception e) {
Log.d(LOG_TAG, "unexpected exception setting temp: " + e);
}

try {
webServer.start();
Log.d(LOG_TAG, "started Web server @ " + getPublicInetAddress());

NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
Notification notification = new Notification( R.drawable.web_server_icon, "WebServer", System.currentTimeMillis());
notification.flags |= Notification.FLAG_ONGOING_EVENT; // Notification.FLAG_NO_CLEAR;
Intent notificationIntent = new Intent(this, MyApp.class);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
notification.setLatestEventInfo(getApplicationContext(), "Web Server" , "Web Server Listening @ " + getPublicInetAddress(), contentIntent);
notificationManager.notify(NOTIFICATION_ID_WEB_SERVER, notification);
}
catch (Exception e) {
Log.d(LOG_TAG, "unexpected exception starting Web server: " + e);
}


DexWebInfConfiguration.java


/**
* Adds dex support to the classloader used in the Jetty WebInfConfiguration.
*
* webapp.setConfigurationClasses(new String[] { DexWebInfConfiguration.class.getName() });
*/
public class DexWebInfConfiguration extends WebInfConfiguration {
/**
* The parent preConfigure is going to load the JAR and class files in so we
* need to adjust the class loader before
*/
@Override
public void preConfigure(WebAppContext context) throws Exception {
String tempDir = getCanonicalNameForWebAppTmpDir(context);
if (tempDir != null) {
tempDir = Environment.getExternalStorageDirectory() + "/" + tempDir;
String dexZip = tempDir + "/webapp/WEB-INF/lib/classes.zip";
context.setClassLoader(new DexClassLoader(dexZip, tempDir, null, getClass().getClassLoader()));
Log.d("Jetty", "Added DexClassLoader for " + dexZip);
}
super.preConfigure(context);
}
}

Saturday, June 04, 2011

running embedded Jetty in Android app

I expect that we'll see this more commonly as more Android-based hardware devices come out other than mobile phones. In this particular case, the Jetty server is intended to drive a dynamic Web-based management tool for an application running on an Android device. I need to get a proper Web app project set up that pre-compiles the JSPs before they get to the Android device but this post is intended to show what libraries are required and how to set up Jetty to run on the Android device. I'm using a regular Android emulator out of Eclipse and a G1 running Cyanogen (API 8) for testing at this point.

General Stuff


I had to use Jetty version 7.3.0.v20110203 to avoid XML validation feature that causes troubles on Android 2.2 devices.

I merged JARs from the Jetty download into a single one to add to my app that I called jetty.jar. For example, here's a little shell script I used for my WAR Deployed Web App.


jar xf ../jetty-continuation-*.jar
jar xf ../jetty-http-*.jar
jar xf ../jetty-io-*.jar
jar xf ../jetty-security-*.jar
jar xf ../jetty-server-*.jar
jar xf ../jetty-servlet-*.jar
jar xf ../jetty-util-*.jar
jar xf ../jetty-webapp-*.jar
jar xf ../jetty-xml-*.jar
jar xf ../servlet-api-2.5.jar
jar cf ~/Documents/ECLIPSE_PROJECT_PATH/libs/jetty.jar *


I see that a work-around for the IPv4 / IPv6 issue is still required. I used

// work-around for Android defect 9431
System.setProperty("java.net.preferIPv4Stack", "true");
System.setProperty("java.net.preferIPv6Addresses", "false");


Since the emulator uses slirp for the guest networking, you have to set up port forwarding from the host. For the G1, I used the real address of the phone (shows up in the status bar). So for the emulator I'd point my browser at http://localhost:8080/ but from my G1 I'd point at http://10.1.1.8:8080/ (of couse, this IP is local to my LAN).

adb -e forward tcp:8080 tcp:8080


Simple Web App


Just to make sure that I could start a server and have it listening as expected, I deployed a simple static app.

This is the list of JARs that I had to put into my app.

  • jetty-continuation-7.3.0.v20110203.jar

  • jetty-http-7.3.0.v20110203.jar

  • jetty-io-7.3.0.v20110203.jar

  • jetty-server-7.3.0.v20110203.jar

  • jetty-util-7.3.0.v20110203.jar

  • servlet-api-2.5.jar



Here is the Jetty server clip I put in my app onCreate

webServer = new Server(8080);

Handler handler = new AbstractHandler() {
public void handle(String target, Request request, HttpServletRequest servletRequest,
HttpServletResponse servletResponse) throws IOException, ServletException {
servletResponse.setContentType("text/html");
servletResponse.setStatus(HttpServletResponse.SC_OK);
servletResponse.getWriter().println("<h1>Hello World</h1>");
((Request) request).setHandled(true);
}
};
webServer.setHandler(handler);

try {
webServer.start();
Log.d(LOG_TAG, "started Web server @ " + getPublicInetAddress());

NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
Notification notification = new Notification( R.drawable.web_server_icon, "WebServer", System.currentTimeMillis());
notification.flags |= Notification.FLAG_ONGOING_EVENT; // Notification.FLAG_NO_CLEAR;
Intent notificationIntent = new Intent(this, MyApp.class);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
notification.setLatestEventInfo(getApplicationContext(), "Web Server" , "Web Server Listening @ " + getPublicInetAddress(), contentIntent);
notificationManager.notify(NOTIFICATION_ID_WEB_SERVER, notification);
}
catch (Exception e) {
Log.d(LOG_TAG, "unexpected exception starting Web server: " + e);
}


WAR Deployed Web App


The next step was to deploy an external servlet via a WAR file. This turned out to be a lot more difficult than I imagined because of checkin r2770 in Jetty-7. It turns out that an XmlParser validation feature was added to Jetty that Android 2.2 doesn't support. Once I moved to a version of Jetty older than that revision I was able to move forward.

This is the list of JARs that I had to put into my app.

  • jetty-continuation-7.3.0.v20110203.jar

  • jetty-http-7.3.0.v20110203.jar

  • jetty-io-7.3.0.v20110203.jar

  • jetty-security-7.3.0.v20110203.jar

  • jetty-server-7.3.0.v20110203.jar

  • jetty-servlet-7.3.0.v20110203.jar

  • jetty-util-7.3.0.v20110203.jar

  • jetty-webapp-7.3.0.v20110203.jar

  • jetty-xml-7.3.0.v20110203.jar

  • servlet-api-2.5.jar



The code:

webServer = new Server(8080);

WebAppContext webapp = new WebAppContext();
webapp.setContextPath("/");
webapp.setTempDirectory(new File("/sdcard/my-work/"));
webapp.setWar("/sdcard/my-webapps/my-ROOT.war");
webServer.setHandler(webapp);

try {
webServer.start();
Log.d(LOG_TAG, "started Web server @ " + getPublicInetAddress());

NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
Notification notification = new Notification( R.drawable.web_server_icon, "WebServer", System.currentTimeMillis());
notification.flags |= Notification.FLAG_ONGOING_EVENT; // Notification.FLAG_NO_CLEAR;
Intent notificationIntent = new Intent(this, MyApp.class);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
notification.setLatestEventInfo(getApplicationContext(), "Web Server" , "Web Server Listening @ " + getPublicInetAddress(), contentIntent);
notificationManager.notify(NOTIFICATION_ID_WEB_SERVER, notification);
}
catch (Exception e) {
Log.d(LOG_TAG, "unexpected exception starting Web server: " + e);
}


The my-work directory gets created fine. I need to push the WAR file out: for the emulator I used adb push ../my-ROOT.war sdcard/my-webapps

Next Steps



  • I need to submit a patch to Jetty to get a try / catch around the XmlParser validation feature that is causing trouble so we can use more recent versions.

  • I need to set up JSP pre-compiling in my Web project so I can add JSPs to make developing the UI easier.

  • I need to dig into the Android device and figure out how to set the low-port listen capability. Right now, I have to listen on the 1024+ port range so I use the common port 80. For a real device, I need to change this to port 80 but that requires a Linux capability change - probably in device boot parameters.



Here's the change that I think needs to be dis-armed a bit.

$ svn diff -c 2770 XmlParser.java
Index: XmlParser.java
===================================================================
--- XmlParser.java (revision 2769)
+++ XmlParser.java (revision 2770)
@@ -107,6 +107,7 @@
_parser.getXMLReader().setFeature("http://xml.org/sax/features/validation", validating);
_parser.getXMLReader().setFeature("http://xml.org/sax/features/namespaces", true);
_parser.getXMLReader().setFeature("http://xml.org/sax/features/namespace-prefixes", false);
+ _parser.getXMLReader().setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", validating);
}
catch (Exception e)
{

Monday, May 24, 2010

Eclipse plugin installation with Proxoid on G1

I had figured I'd just be browsing via the tether but then I discovered that I needed another Eclipse plugin. Setting Eclipse to use the proxy was simple enough. I use Proxoid on the G1 and adb on my computer just like I did in my previous tethering post. Rather than start Eclipse with a proxy configured, I adjusted the Eclipse preferences to use the proxy. The risk of adjusting the preferences is that when I'm in the office, I'll forget that I had configured the proxy - my memory for trivial stuff is really that bad.

Eclipse -> Preferences... | General -> Network Connections


You have to change the drop-down the Manual before you can edit the HTTP and HTTPS proxies. Since I have adb mapping 8080 for the proxy, that's what I am using in this proxy. Once the proxy is set in Network Connections, plugins installed through regular means.



Good Stuff.

Network Connections for All
I had to install the 1.6 platform so I could create a new app for my phone. Since the Android plugin manages the platform and API installations, I wasn't real confident that the Network Connections proxy would get used. I was pleased to find that Network Connections does affect the ADT properly - I'm somewhere in Iowa between Des Moines and Council Bluffs and getting the platform.

tethered via G1, Proxoid, and Chrome

It is great to hear that tethering will be part of the OS on future phones. For now, tethering isn't too bad - the worst bit is getting the Android SDK installed if you don't have it already.

Proxoid app on G1
My G1 is using Android OS 1.6 and I doubt it will ever get the 2.x line. There are probably a bunch of tethering apps but the one I downloaded was Proxoid. When you open Proxoid, there is basically just an option to change ports and then a start / stop button. It crashed once but otherwise it appears reliable. When I was to tether my computer, I open Proxoid and click Start.

Android SDK
I assume that all tethering is the same: you start the proxy app on the phone and then use adb from the Android SDK to forward some TCP port on the computer to a port on the phone. Download and install the latest Android SDK and then add the tools directory to your PATH.

I forward TCP port 8080 on my computer to port 8080 on the phone. Port 8080 is currently the default port that Proxoid uses on the phone.
adb forward tcp:8080 tcp:8080


Chrome browsing
It isn't necessary to use Chrome but it is easy to open Chrome using a proxy and not have to change any settings. You could easily do this with FF or Safari if there is a command-line option to use a proxy or if you find the proper configuration settings.

/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --proxy-server=localhost:8080

Tuesday, May 04, 2010

touchstart and touchmove

For the most part, HTML5 apps test comparably in Safari and on the iPad. One difference that really jumps out is the touch events that the iPad / iPhone implements. My application initially bound events to the click event but I noticed a delay in the iPad so I switched over to the touchstart event. When the application is running in iPad / iPhone, the whole canvas is draggable and looks crappy.

From reading blogs and SO, it seems like the delay in onClick is intentional in iPhone / iPad because the browser (UIWebView, in this case) is waiting to see if there is going to be a gesture. That makes sense to me so any cases where I had a .click I changed to a .bind(clickevent. I set clickevent = click by default but override with clickevent = touchstart when on iPhone / iPad.

In the image below, you can see a big gray space - thats from clicking and dragging the screen down. After implementing the event.preventDefault the view can no longer be dragged down.


$ git diff HTML/main.js
diff --git a/HTML/main.js b/HTML/main.js
index 4e7661b..5bceab2 100644
--- a/HTML/main.js
+++ b/HTML/main.js
@@ -23,6 +23,14 @@ if(window.Touch) {
clickevent = 'touchstart';
}

+// TODO - can combine with other iPad / iPhone checks but keeping separate
+// for now just to make it stand out
+if(window.Touch) {
+ document.ontouchmove = function(event) {
+ event.preventDefault();
+ }
+}
+
$.ajaxSetup({
beforeSend: function(xhr) { xhr.setRequestHeader("Emsauthtoken",localStorag
cache: false,


Friday, April 30, 2010

Palm and WebOS

I wish things would have gone differently with Palm and WebOS. Reading almost nothing but negative press about the HP buyout of Palm has me thinking about it. From my perspective, Palm committed where RedHat, Google, and probably others just talk big about: they built devices that were really built on Web technologies. I've said before how impressive it was that Palm shipped their VM on VirtualBox - it was a specific, older version of VirtualBox - but it wasn't some opaque, crappy device simulator that other vendors provide. Building apps for devices with just HTML/CSS/JS is a great solution and it is supported in Android and iPhone but Palm made it THE solution.

My favorite quote found reading comments in the negative press:

At one time you could have said Apple was late since Palm already had the Smart Phone market cornered.


Heh - it really is true. Today, seems like the press and most of the noisy people are Apple fan boys / girls but tomorrow it won't be the same and the 'obvious' stupidity of HP buying Palm may or may not be true.

WebOS will probably die at HP but it was a great effort by Palm. Shame.

Thursday, March 11, 2010

HTML5 Buckets

Buckets has been idle for many months and I still haven't gotten Shanelle using it. Something - I think it was Luke - got me thinking about finishing the iPhone version. In fact, starting and then finishing the iPhone version :) I feel the need for doing it in HTML5 - this app is a good candidate for a non-native application implementation.

I started looking around for demo apps that had the components I need and handily bonked into webkit.org/demos/sticky-notes/ I dream of a competitive WebOS device so it is nice that iPhone and WebOS should have very similar implementations. Here's the first bit of goodness.

$ curl --include http://webkit.org/demos/sticky-notes/StickyNotes.manifest
HTTP/1.1 200 OK
Date: Thu, 11 Mar 2010 06:01:37 GMT
Server: Apache/2.2.14 (Unix) mod_ssl/2.2.14 OpenSSL/0.9.8l DAV/2 mod_python/3.3.1 Python/2.5.4
Last-Modified: Sat, 13 Jun 2009 00:53:08 GMT
ETag: "38-46c303f980500"
Accept-Ranges: bytes
Content-Length: 56
Content-Type: text/cache-manifest

CACHE MANIFEST
deleteButton.png
deleteButtonPressed.png


There are some important rules going on - this bit is telling the browser exactly what resources to make available when the application is in offline mode. Sticky Notes is mostly CSS so the manifest is micro but it's still a good start. Some bits that I've picked up in other places: the page that includes the manifest= is automatically whitelisted; fallback is how you replace resources with an offline-specific image or resource; the content of the manifest has to change for the browser to recognize a difference - it isn't the timestamp or the filename / path.

Sunday, February 28, 2010

where are symbol-based domains

It occurred to me that I've never seen a symbol-based domain in use. For example, why haven't I ever seen a symbol in a domain - especially some domain like http://www.i♥android.com/?

As a side note - I've read that Windows people would see the Windows logo in this domain and Mac people would see the Apple logo. What do you see? http://i♥.com/.


From my late-night skimming (I'm saying I didn't really read much - this is what I've gleaned) it looks like in 2005 DNS switched from ASCII to Unicode. The support for Unicode domain names appears to be there in all common browsers. I tested Chrome, Safari, Firefox, and IE and all of them behaved the same way.

It seems like people should have started using symbols for fun. No doubt there are non-US domains that require unicode but I'm just thinking about the vanity type stuff. It looks like Network Solutions briefly allowed symbols in .com domains (£.com and ♥.com) but doesn't anymore. Out of curiousity, I attempted to register i♥android.com at a few registrars and was consistently blocked. I then attempted to register i♥android.ws at 3 registrars and 1 allowed it (GoDaddy). Interesting.
Safari hits symbol-based domains nicely.

Safari hits symbol-based domains nicely. My version of Chrome hit my play domain correctly but then munged the URL. Silly.

Speaking of silly - let's say that people were using symbol-based domains for vanity stuff or marketing - in both cases, they'd probably want to use the vanity names in social sites. Why aren't usernames allowed to contain unicode? I only checked a few sites and they only wanted simple ASCII. I wonder if there is a reason that I'm unaware of or if it is just easier for devs to not think about more complicated usernames.


Monday, February 15, 2010

Ike and Spence getting started with Game Maker 8

Isaac and Spencer have been playing Club Penguin and Build-A-Bear-Workshop a lot lately. One day Spencer decided that it is time for him to write his own game and he started sending me ideas in IM. I asked around and looked around and decided that Game Maker 8 looks like a good tool for Spencer to use. I had the boys read through a tutorial on their own and then yesterday we went through a tutorial together and created a simple game.

Isaac and Spencer working hard. They each liked different speeds, sounds, and backgrounds but ended up with basically the same game.


Asher was pretty impatient with us working on the games. Asher loves playing Club Penguin so not only were we not playing with him - he couldn't use a computer while we worked. He went around taking picture of just about everything in the house. Here's the only decent one though :)


So for a simple game, you pick out sprites and sounds and identify them as resources. Then you set up objects and boards using the sprites and sounds. The objects are given events and actions associated with the events. For example, a create event is used to start some objects moving around on the board. A collision event just gets a bounce in our game. There are more events and actions than I could ever have imagined.

I hope the boys continue forward and do more playing. I imagine I'll have to work with them some more but I think they could do some more simple stuff on their own.

Thursday, February 04, 2010

started soldering protoduino prototype

I had planned on testing with a breadboard at the next FTH test but then I decided that parts would probably jiggle loose. Michael and I discussed options and Michael found the Protoduino. For $13, it is a spectacular deal and very convenient. I started soldering the components to it tonight but this bit goes quite slow.

I've got the boost interrupt (rectifier and long blue wires), 5V voltage regulator, and reset switch in. I attached a 3 pin molex connector to the pressure transducer. I still have to put the servo connector, power connectors, data logger header, and 6V voltage regulator on. It'll be tight but everything will just fit.

Wednesday, January 27, 2010

using RogueSD Arduino lib after SMD exercise

Michael discovered that Rogue Robotics has an Arduino library for the uMMC data logger unit that I have. My uMMC is about 4 years old so I wasn't too surprised that it didn't work when I hooked it up and updated to the latest firmware.

I have a uMMC unit that I successfully upgraded to firmware
102.08-b004 so that I could use the Arduino libraries.
The commands to changing and reading the settings work and the version
command works but Z always returns E05. I've tried 3 different cards
that all worked before upgrading from 101.56 and now they all fail to
initialize. I've tried different baud rates (9600 to 115200) but they
all fail. Does this device support 102.08? Is there anything else I
can do to trouble-shoot this? The Arduino library page says that I
can use 101.56 but the code says I have to have 102.01 at minimum -
which is it? Could you point me at 102.07 so I can try that version?


I mailed support the above message and received a quick response. I ended up sending a picture of my unit and they sent me back my picture with an edit showing where I needed to add a resistor. Great help from RR - quite impressed with the company.


I was a bit nervous about the SMD soldering but it was no problem. I shaped the resistor leads so it fit nicely then I put a bit of flux on the 2 pins that I had to solder the resistor to. I decided to use some fine lead-free solder since I had the extra flux on the pins and that worked out okay. Once I soldered the resistor in, I tested the uMMC using the old firmware and then I upgraded the firmware and tested - it worked! :)


Here's the code I used for testing the uMMC with RogueSD and the latest firmware.

#include "NewSoftSerial.h"
#include "RogueSD.h"

#define COM_BAUD_RATE 115200

#define DATA_LOGGER_BAUD_RATE 9600
#define DATA_LOGGER_FILENAME "/uMMCtest.csv"
#define DATA_LOGGER_RX_PIN 3 // ATmega168 pin 5
#define DATA_LOGGER_TX_PIN 4 // ATmega168 pin 6

// using hardware serial interface for cli communications
static HardwareSerial& comSerial = Serial;
// static NewSoftSerial comSerial(DATA_LOGGER_TX_PIN, DATA_LOGGER_RX_PIN);

// the file handle used for logging; will be > 0 if uMMC present
static int dataLoggerFileHandle = -1;

// The firmware assumes the uMMC API for the data logger.
static NewSoftSerial dataLoggerSerial(DATA_LOGGER_TX_PIN, DATA_LOGGER_RX_PIN);
// static HardwareSerial& dataLoggerSerial = Serial;
static RogueSD ummc(dataLoggerSerial);

void setup() {
comSerial.begin(COM_BAUD_RATE);
comSerial.println("uMMC Test Booting...");

dataLoggerSerial.begin(DATA_LOGGER_BAUD_RATE);
comSerial.print("uMMC Version: ");
comSerial.println(ummc.version());
ummc.sync();
dataLoggerFileHandle = ummc.open(DATA_LOGGER_FILENAME, OPEN_APPEND);
if((dataLoggerFileHandle <= 0) || (ummc.LastErrorCode != 0)) {
comSerial.print("uMMC Error: ");
comSerial.println(ummc.LastErrorCode, HEX);
}
}

void loop() {
if(millis() > 20000) {
if(dataLoggerFileHandle > 0) {
ummc.close(dataLoggerFileHandle);
dataLoggerFileHandle = -1;
comSerial.println("Closed Data Logger");
}
} else {
writeToDataLogger();
}
delay(4000);
}

void writeToDataLogger() {
if(dataLoggerFileHandle <= 0) {
return;
}

int chamberPressure = millis() >> 8;
int tankPressure = millis() >> 8;

unsigned long dataLoggerStartTime = micros();

ummc.writeln_prep(dataLoggerFileHandle);
ummc.print(millis(), HEX);
ummc.print(",");
ummc.print(chamberPressure, HEX);
ummc.print(",");
ummc.print(tankPressure, HEX);
ummc.print("\n");
ummc.writeln_finish();
if((dataLoggerFileHandle <= 0) || (ummc.LastErrorCode != 0)) {
comSerial.print("uMMC Error: ");
comSerial.println(ummc.LastErrorCode, HEX);
}

comSerial.print("Micros: ");
comSerial.println(micros() - dataLoggerStartTime);
}

Sunday, January 24, 2010

sewing with Greg

I went over to Greg's tonight to burn our propellant-filled straws in the strand-burner. The first task was to sew fine wires in the propellant so they can be used to determine the burn rate.



The strand burner is collecting 2 different bits of data: the speed that the propellant is burning through the straw and the pressure in the burn chamber. In the picture below, you can see the gigantic pressure transducer sticking off the burn chamber and the wires all connected to the other end. Everything goes into the box on the floor which then goes to the computer via the DATAQ.



Speaking of pressure transducers, Greg got a couple of these spectacular, tiny transducers!



Good stuff.

Saturday, January 09, 2010

Scouts at EcoCycle

Unloading for people.  The training about what and why is really interesting.

Posted via email from mrtidy's posterous

Shelved the PIC

I've heard from Kelly and then from Ken that the Arduino is the way to go for a hobby embedded project. I had been using the PIC for the FTH project but Michael and I discussed the Arduino and decided we really should take the advice. I bought the ATmega168 with Arduino bootloader installed and built the supporting circuit around it and was up and running within a couple hours. The Arduino is as impressive as people made it sound - seriously good stuff.

Michael started looking at the MsTimer2 library to easily hook into the Timer2 so we can get a reliable interrupt for reading the pressure transducers. I put that into the app and did some testing with delays and Serial printing to verify that Timer2 really would interrupt loop, delays, and Serial. It looks good so far.

Here's a clip of code that sets up Timer2 to just print 'Reading Transducer.' and then a Serial call that should happen about the same time as the interrupt. I got lucky in the timing and feel like it proved itself.


void setup() {
Serial.begin(BAUD_RATE);
Serial.println("FTH-Arduino Booting...");
...
// Read the transducers @ 1kHz
MsTimer2::set(1000, readTransducer);
MsTimer2::start();
}

void loop() {
delay(4000);
Serial.println("Boo");
...


void readTransducer() {
Serial.println("Reading Transducer.");
}


Here's the serial output.


FTH-Arduino Booting...
Reading Transducer.
Reading Transducer.
Reading Transducer.
BoReading Transducer.
o
Reading Transducer.


I've still got the PIC code in Bit Bucket and may build something with the PIC in the future but for now I'm loving the Arduino. Here's the current breadboard.

Sunday, January 03, 2010

Motor test

Download now or watch on posterous
VIDEO0007.3gp (1400 KB)

First video of John's motor test.

Posted via email from mrtidy's posterous

Saturday, December 26, 2009

sledding fun

Has fun sledding today - great hill!  Adam and Griffin were there so that was extra fun for the kids.  Blake and Kimberly sledded a bit with Asher and even pulled him up the hill a couple times!  :)

Posted via email from mrtidy's posterous

Friday, December 18, 2009

moving servo via pot; even has a blinking LED

After 2 days without electronics due to illness, I am feeling better and was happy to pull out the stuff. Shanelle wasn't so happy but we watched a movie while I played. I read the oscillator section in the 18F2455 datasheet and felt a lot more comfortable with the oscillator config; in addition, I learned that PWM uses timer 2 so I learned about the pre / post scalers there. I set everything up and was confident that I had the correct timing values but wasn't getting reliable servo movement (servo wasn't dancing either so I was thinking that I had made progress). Michael suggested batteries; I set up an LM7805 and tried driving the servo off my crappy power supply again but using 12V + LM7805; servo still pulsing; Michael suggested batteries. I tried batteries on the servo and now had a stable servo :)

I added a potentiometer to the circuit and adjusted the code to read the pot and position the servo accordingly. I put a video on youtube and then shortened the delay between reads. We're really getting somewhere now :) This code is responsive and good enough to pass along to Greg for testing with a valve and servos while I work on it more to actually considering the pressures and what they mean.




#include
#include
#include
#include

// app uses internal oscillator, RA6 for IO
#pragma config FOSC = INTOSCIO_EC
// OSCCON = 0b01110000; should switch to 8Mhz ??

#pragma config BOR = OFF // Brown out reset
#pragma config LVP = OFF // Low voltage programming
#pragma config WDT = OFF // Watchdog timer

// use pin 11 for basic LED indicator
#define LEDPin LATCbits.LATC0
#define LEDTris TRISCbits.TRISC0

void main() {
// pre / post scalers at 1:16 / 1:2
T2CONbits.T2CKPS1 = 1;
T2CONbits.T2CKPS0 = 1;
T2CONbits.T2OUTPS3 = 0;
T2CONbits.T2OUTPS2 = 0;
T2CONbits.T2OUTPS1 = 0;
T2CONbits.T2OUTPS0 = 1;

// the period is 25ms
OpenPWM1(250);

// TODO - understand all these flags
OpenADC(ADC_FOSC_8 & ADC_RIGHT_JUST & ADC_0_TAD,
ADC_CH0 & ADC_INT_OFF & ADC_VREFPLUS_VDD & ADC_VREFMINUS_VSS,
0b1011);
SetChanADC(ADC_CH0);

LEDTris = 0; // make sure LED pin is an output pin
LEDPin = 1; // turn LED on
while(1) {
LEDPin = ~LEDPin;
Delay10KTCYx(5); // (.2s delay between reads)

ConvertADC();
while(BusyADC());
// through experimentation, I found that a 'duty cycle' between
// 18 and 81 was about right; my pot in my circuit is giving
// 0 to 4.2V so I'm multiplying by 1.2; the ReadADC returns a
// 10 bit result so dividing by 16 to put me 0 to 63
SetDCPWM1(18 + ((int) (1.2 * ReadADC()) >> 4));
}
}