Sunday, February 24, 2013


Remote Command - updated!

Wow, two blog updates in one day! Aren't I on top of things today!

I updated the server so that one can now use a pipe in order to send back messages to the log window. A new optional keyword is added to the configuration file as 'pipe', where you can specify a pipe to use. You need to create the pipe before the program starts with 'mkfifo'. When the server is started you can just direct output directly from the command or within a script to the pipe, and it will be received by the server.

New keyword 'pipe'.
The script './comtest' redirects its output to the pipe.


The server's log window

But now I'm really done with this for today. G'night!

David

Remote Command

I know, this screenshotting thing isn't really my thing... Or GUIs, rather. I like to focus on the functionality, and do UIs later. If I ever need to.

Anyway, this is a Unix server, remotely controlled by an Android client app over TCP/IP. You give the server a list of commands, and use the client to pick a command to execute. The server then executes the command, and returns an exit value. Quite simple stuff. The screenshots gives you an idea of how it works.

Server configuration file
Server startup
Server help screen
Server list of commands

Server after execution of a few commands
tensecs script
Client startup
Client setup


Client after refresh
Client command list

Client after a few executions
First, you configure the server by creating a configuration file. By default it is named server.cfg and is found in the same directory as the server is run. Alternatively you use the -c flag to specify a different name and location.

The configuration file has a simple layout that is frequently seen on Unix systems (see screenshot). Blank lines (that is, empty lines or lines containing only whitespaces) are ignored, as are comments that start with a hash (#) symbol. Otherwise you use the very common key-and-value syntax, where a key in this case is either 'ip', 'port' or 'command', and values are specified by double quotes. ip and port are mandatory; if they are not found, the server will not start. It's quite self-explanatory, but the ip and port specify what address and port to listen to, and if they cannot be bound to a socket, the server will close down. command specify a command that you will want the client to be able to execute. Naturally, you can enter as many commands as you would like.

By pressing 'h' you get some help messages (as seen in the screenshot), and by pressing 'l' (see another screenshot) you get the active list of commands. Here you can also choose to execute commands locally if you wish, perhaps for debugging purposes.

And so, the client. First, press 'setup' to configure the server IP address and port, then go back to the main window, by pressing save, and in the main window press 'refresh'. You should now see the very same log in the 'Remote' window on the client as you see in the 'Log' window on the server.

This function (refresh) is what you need to use every time you want to see whether something has occur on the server. The connection to the server closes down after each refresh is made, so the server has no means of informing the client that something has happened; it is something you need to find out for yourself, by pressing 'refresh'.

After having pressed 'refresh', you can choose a command to execute by pressing 'command'. When doing so, the client just asks the server to run the command. It doesn't even receive a reply. You need to press 'refresh' again to see what happened on the server, if the command was indeed executed, and if so, returned with what exit value.

I chose this somewhat unsophisticated networking approach this time, partly due to the simplicity of the code but also for practical purposes. I didn't want to stay connected for longer than necessary, since the client is run on a mobile device where a longer time and more data transfer means higher costs for the user, and it might indeed take long before an execution of a command returns. It's perfectly conceivable that another server is subsequently started by this server, and so might not return for an extended period of time.

The execution of commands on the server is threaded. That is, even if a command doesn't return immediately, the server continues to be usable for other requests. This is illustrated on the last screenshot, where a command named './tensecs' is executed and only returned after a few other commands have been executed and returned in between.

However, the networking is not threaded. I didn't think that was necessary due to the very short time that a connection stays active.

So, where to go from here?

Naturally, it could be extended in a million ways. The first thing that comes to mind would be a nice way for commands to return an output string back to the debug window on the server. Don't know exactly how this should be done, but maybe one could use a Unix pipe? This is probably the first thing that should be improved later.

Maybe, also, the networking should be redesigned. One could have the option of letting the socket stay connected until the app closes down or the screen goes blank, or something similar. Encryption should definitely be implemented. As I understand it, SSL encryption is not very hard to do in C or Java/Android, so that could be a good fix for that.

I had a friend of mine suggesting that the client should be made a plugin for a popular Android app called Tasker. That should also be looked at. A better-looking GUI would be nice too, I suppose.

Oh, and I have to whine about something too! I had a memory leak during the development of the server that took some time to track down. (Thanks Elvis for the help with that!) During the debugging I discovered that ncurses apparently does not free all the memory it allocates! Making a test application doing only a initscr() and endwin(), and I saw that there's still memory that could be free()'d! The FAQ of ncurses apparently states that this is normal and notes "That makes it hard to analyze curses applications for memory leaks." Yeah, no shit! But you could recompile the library with some debugging options in order to force it to free its resources. I dunno, but to me that seems like something that should be a default option...

Anyway, for now it the way I needed it to. All I wanted was to be able to have a simple means of controlling my Linux server from my Android phone, and that's exactly what this is!

And again, the source code for both the server and client will be published on my website shortly!

David

Monday, January 28, 2013

Bluetooth


What a mess.

But finally, it works.

Yeah, yeah, I know, it's not much of a screenshot gallery, but I figured it was better than nothing! I probably should have made a small video clip, but since the only video recording device I currently possess happens to be the very phone that I want to tape, you'll have to make do with the images below.

When you tilt the phone by different angles, the server simulates a movement of the robot accordingly. Looks simple, sounds simple, but was hell to make!
Starting the server...
Connection established, client side
Moving forwards to the right!
Connection established!
Moving forward to the right!

I am currently taking a class called Electronics Project, which is just about what it sounds like. We're working in groups of five to seven people and each group gets an assignment that has to be finished before the summer. All of the assignments are about building robots of some kind. Our robot will be one that shall find its way through a labyrinth, guided by stickers on the ground. Somewhere in there it will find an item, pick that item up, and autonomously (without stickers guiding it!) bring it back out of the maze. That means, it needs to remember where it came from.

Anyway, before we get to the point where the robot can actually orient all by itself, we need a means to control it manually. And this is where Bluetooth comes in. It's mandatory to use this very protocol for whatever data is to be transmitted between the robot and the controller.

So, what this is, is an Android app that can be used as a mobile stand-in for the computer that will otherwise control the robot, and a robot simulator. I figured we could use these two as debugging tools when we create the real thing later on.

Man, has this been a challenge! I have made some very (very!) simple Android apps in the past, so at least I knew where to begin, and I did know that Google has tried quite hard to make things as easy as possible for you to program Android, so I figured that this Bluetooth thing shouldn't be much of a headache.

Wrong.

It would probably have been much easier had I stuck to the same platform for both the client and the server, but since Bluetooth is such a widely supported standard I thought that shouldn't be an issue. And besides, since the robot uses Atmel megaAVR computers that are to be programmed in C, I hoped that having done the server in C once would be helpful once time comes to program the actual robot. I've realized afterwards that this might not be true, since the APIs are completely different in each implementation. And I have as of yet no idea how much of a library is provided on an AVR...

Anyway, the simulator is running on a Linux host. It uses the BlueZ stack for Bluetooth communication and ncurses for the very beautiful user interface.

BlueZ is a very complete Bluetooth stack. Only problem is the severe lack of documentation! There are quite a few tutorials on the Internet to get you started, but once you want to dig deeper into the return values of the more obscure functions and the less commonly used arguments of its helper functions, you're on your own. I know, the source code is available, but I'm really not confident enough in C to go there.

Then I found Bluetooth Essentials for Programmers by Huang and Rudolf. I would never have made it without this book! Everything is laid out

It also turned out that the Andriod Bluetooth API wasn't all that complete. And it also had its fair share of undocumented methods. For some reason I do not yet understand, the SDP requests always failed, and I was forced to use one of those undocumented methods (createRfcommSocket(int channel);) in order to bypass SDP completely and open a RFCOMM connection on a specified channel directly. Not a very beautiful solution. I'll have to find out a better solution to this before the end of the project.

I'll post more of the technical details later on my website, including the source code of all the software involved, but for now I just wanted to make a quick demonstration of what I have done so far.

David

Tuesday, January 15, 2013

Tuesday, July 21, 2009

我学了两年多了,我的中文还是不好。为什么中文这么难?