Showing posts with label programming. Show all posts
Showing posts with label programming. Show all posts

DMX for actual Electrical Engineers

So it's been coming up recently that our church media department has been buying "OpenDMX USB Dongles" for upwards of $80. I was mortified, especially after seeing this USB device, opening up the sheet metal project box and finding it to be a simple 2-stage USB to RS-485 protocol bridge with no programmable intelligence whatsoever.

During one church service, I counted out up the components in one of these boxes, along with the cost to manufacture around 12 PCBs from a 6"x6" dual-sided copper clad (including etching and tinning my own board), dumb down the expensive 5-pin connectors to a cheaper 3-pin (technically non-compliant according to ANSI, the agency that manages the DMX-512 standard that every designer is supposed to follow). The total cost of materials, given that my labor would be, along with everything else I do for the church media department, strictly volunteer-based, came to around $20 - a quarter of what it costs them to order the pre-made one.

And talking with the church employees, the lights themselves have not been acting right, and we're using such-and-such software from a computer to control them. Blah blah. On it goes.

Now these church employees ARE really smart. I mean, hey, they managed to set the entire network of DMX lights up, possibly with the help of a contracted expert, and have been training volunteers to program and control the network on the fly. But the terminology, and the lack of basic electronic knowledge has inspired me to make a write-up of what I've been learning lately.

Especially when last night I viewed THIS attrocity.

This just tells me that typical users simply don't know what in the world they're doing when they sink hundreds or even thousands of dollars into a system that requires the technical know-how of a digital electrical engineer.

Therefore I will see what I can do to write down my own experiences and discoveries and hopefully someone else can benefit.

Protocol

DMX is an 8-bit (technically 11-bit) digital protocol sent over a chained RS-485 bus to up to 512 slave devices. The DMX standard says that technically, only 48 devices can be chained directly together, but this can be expanded up to 512 by use of splitters and re-transmission. Also, the last device in a chain should be terminated with 120-Ohms.

This chain of devices is known as a "universe," but can be thought of as a bus.

RS-485 is the lowest level for bit control above which all modern-day networking is performed. In short, the base protocol that connects a computer to the internet via TCP/IP is the same protocol that connects a DMX controller (sometimes software within a computer) to a chain of DMX devices.

Addressing

A single physical device can consume any number of addresses (channels) off of the bus. Each device is physically programmed (probably with a 10-pin dip switch) with a start address after which its sub-devices simply count up from that number.

The controller is automatically given address 0.

A slave device (let's say a 4-color light with X-Y movement) is assigned in the controller with a start address and then consumes additional channels based on what it can do.

For example, an RGBW canister with X-Y movement could consume 6 channels - one for each color, one for the X stepper motor, and one for the Y stepper motor. The canister that my dad is currently working on fixing uses 7 channels. The additional channel is used for beam focusing with an additional stepper motor that moves a glass lens up and down in front of the light.

Therefore, in a controller, one can only assign devices to an address starting at 1. A second device should not be assigned to a channel that could be taken up by an already used channel, even if it is not explicitly allocated by the user, but instead has been taken up by a multi-channel device.

As in the example above, we could assign Light #1 (a 7-channel device) to Address 1. But a second light of the same type would need to be assigned to Address 8.

AddressDeviceFunction
0ControllerMain control of DMX512 bus
1Light 1Red Intensity
2Light 1Green Intensity
3Light 1Blue Intensity
4Light 1White Intensity
5Light 1X Stepper Position
6Light 1Y Stepper Position
7Light 1Focus Stepper Position
8Light 2Red Intensity
9Light 2Green Intensity
10Light 2Blue Intensity
11Light 2White Intensity
12Light 2X Stepper Position
13Light 2Y Stepper Position
14Light 2Focus Stepper Position

Wikipedia's oscilloscope picture has been instrumental in helping me understand the digital commands required. However, it was also confusing in that I did not understand that this was a simple UART packet transmission. It was only when I opened that $80 DMX bridge and looked it over that I realized the simplicity of being able to set up a master controller with software alone.

But in short, when a DMX packet is sent, it is initialized with a "break" (logical 0 a minimum of 92us long) followed by a "mark after break" (logical 1 a minimum of 8us long). This tells the devices chained on the bus that the bytes to follow should be counted from 1 to 512 in order, and that they will execute the byte value (from 0 to 255) for the channel(s) they consume.

One can send a partial packet, as long as the break and MAB is detected to reset the bus. A packet must be sent repeatedly or devices should enter a "reset" state. So if the bus master is turned off, all devices on the bus should stop functioning, turn off, or go into an idle mode.

Addresses versus Channels


It is important not to mix up the device bus positions (addresses/channels) with the controller sliders/knobs/adjustments you assign them to. Many hardware controllers make this especially confusing by also calling the sliders "channels" as well. In a controller's internal software, the user is supposed to first map all devices with their assigned addresses to slider channels that the controller itself defines internally.

With most controllers and controller software, it is possible to assign multiple hardware addresses to a single controller channel.

Controllers are ARBITRARILY PROGRAMMED

It is important to note that, like the video above, it is often misunderstood that a physical slider on a hardware controller will not always change the intensity of a light as one would expect to change the volume on an audio mixer. What happens for each value in each channel that a device uses is up to the slave device's manufacturer.

I had the nightmare of trying to figure out light control on a Zero 88 Fat Frog controller without first understanding that "what the device does is defined BY THE DEVICE!" That the programmer had taped over some button labels to define them as "smart lights" did not make sense because I had failed to read the user manual for the beam-shaping capability of the gobo canister hung on the rail in the ceiling. And I had no knowledge of what addresses they were assigned or what sliders they had been programmed to. Because what slider or knob or software adjustment does is defined by what address YOU assign it to control in regards to the devices connected to your bus.

This brings us to...

User Interface

When I started talking with people, and reading the DMX512 standard and related Wikipedia articles, I realized that MOST hardware controllers have defined a set of manufacturers and manufacturer devices so that the setup of a controller can be easy and straightforward. Simply tell the controller that on Address 16 a Chauvet EVE E-100Z and now it knows that it has the option to access the light on Address 16 for a single-channel dimmer or on Address 16, 17 and 18 for dimmer, strobe and dimmer speed mode. Then one simply assigns Chauvet EVE E-100Z addresses to slider 1, 2 and 3, or one assigns Slider 1 to Chauvet EVE E-100Z dimmer #1, #2, and #3 along with the Chauvet DJ SlimSTRIP UV-18 blacklight wall wash unit.

But what if your software or controller doesn't have these devices available in its repository of knowledge? If you know your device and you know your software, it should be extremely simple to just set up a general device in the configuration.

One only need tell the controller that "there is an unknown device that you should send a value to starting on Address 16 and consuming 17 and 18. These three channels you do not know what they'll do. Therefore, simply send a value from 0 to 255 when this slider is positioned just so."

I have a JANDS StageCL controller that I am able to play with on a weekly basis. A few months ago we went into the patch settings to see just how the DMX was set up. It turns out that someone had turned off control of a few of the white spots, and a few of the RGB washes had been shifted out to a different set of slider. This was all viewable in the patch settings. And a few of these devices were unknown to the controller's library, but we were able assign a simple RGB device which basically told the controller that "three addresses are consumed by one device to separate red, green and blue intensity." After that, the controller knew how to best utilize its own controls, such as color and saturation knobs and a fader that you patched the device to.

What was also interesting was the DMX monitor which simply showed the digital values that the controller was constantly pushing out to the bus. It showed nothing of the patching and slider controls. It only showed that we were sending WHATEVER was connected on address 301, 401 and 501 a value of 255. A single 255 could have been "full brightness of green" or it could have been "move x-axis stepper to 0 degrees."

Pragmatic Programming

Yesterday I was looking at software controllers - the cheap way of setting something up. In this case, you have no hardware that buffers commands to the bus, and are relying on your multi-threaded computer to ensure that a packet is transmitted every few milliseconds to prevent the bus from going into reset/idle mode.

My dad found one called Freestyler,which is highly rated for a freeware solution. However, he found that the device manufacturer had to be known by the software. Whether or not one could add an unknown device or define a known set of controls to the software was not researched.

I wanted something even simpler. So I found a very small utility on Sourceforge called Lights Up! Given that this was on Sourceforge, it was also no trouble to download the CVS repository and find out how the source code was constructed (and to fork and re-distribute it on Github). 

This is the most barebones example I've found so far, with 48 sliders that can be assigned to any number or combination of addresses. The only requirement is that an FTDI USB-to-UART chip be registered as a virtual COM port on the PC running the software. The author assumes that this means you have to spend that horrendous $70 on the Enttec OpenDMX USB dongle, but I found that plugging in a simple FTDI TTL-232R-5V-PCB that we use for talking to our A2D boards would suffice. After that it's a simple matter to re-buffer traffic from the RS-232 to RS-485 which is just a digital driver that ensures that you can run devices on a longer distance cable at higher currents.

Looking into the Lights Up! source code, I found that by using FTDI's C++ libraries and a intermediary "odmxusb" class library which handles the 513-byte packet, high priority threading and 30ms repeated transmission, I only need to build a 512-byte array of commands and send them into the data buffer of odmxusb. After that, everything is taken care of. That way, I can build whatever-the-heck user interface I want!

Summary

  • DMX is just an RS-485 UART with an electrical interface that chains up to 512 slave devices together.
  • All devices on the DMX bus read all traffic, but pick out the commands intended for them based on the byte order of the data stream and the address they are pre-programmed with.
  • Because of this a fancy DMX controller, however much of a user interface layer it's given, is at the lowest level, completely arbitrary and could be reconfigured to do Very Weird Things if the protocol is not understood.
  • I want to write my own software from a point near ground-level and work my way up.

Typecasting struct to char* for idiots like me

Want to cast from a struct to a character string? Seen all those posts out there suggesting that reinterpret_cast will work, but is unsafe? Yeah, I tried it. Yeah, it's unsafe.

What about the ones saying you could just malloc()? Well, my own personal mantra says that direct memory manipulation is also rather unsafe if you don't count right or are not careful with stack memory and such. I have avoided malloc() like I have learned to avoid reinterpret_cast.

But just today I discovered a most elegant solution:

struct newStruct { //33 total bytes long
    int dw0;
    uint_16 s0, s1, s2, s3;
    float f0, f1, f2, f3;
    char b0;
    int i0;
};

void byteOperation(char *p) {
    // Treat input parameter "p" like a big character array and do things one byte at a time!!
}

newStruct v;
newStruct w[1];

byteOperation((char*)v); // Compiler error: Cannot cast from 'newStruct' to 'char *'
byteOperation((char*)w); // Will just work.




Embarcadero TThread Just Works

I've been annoyed for a large amount of time (years in fact) over some of our software we use and sell. In essence, what we have is a TCP GUI that communicates on a 250ms timer with a microcontroller that's serving up a two-way communications protocol over ModBus FC23 - we send commands either to write data to or read data from the unit.

The developer we had make the initial software for us decided to use Embarcadero's system timer object to control the 250ms polling. Not only that, but I had to put in an 8-second delay during socket initialization (via a TPing object) in the event that the unit was rebooted (we want to wait until the unit can finish booting before we say that it can't be found on the network). And I learned ALL of my Borland/Embarcadero, and even the majority of my C++ knowledge from this program and others I've patterned after it.

But both the timer and the timeout exist in the main method. So every time the TPing object waits for the unit's response, or every time the 250ms timer hung, the entire program would freeze. It's been very annoying to say the least.

The microcontroller library I use (Netburner uCOS) has a fairly straightforward way to set up "tasks," and particularly the MOD5282 has something called a periodic interrupt (PITR) which will run a very fast clock at some hardware-programmable rate that will set an IRQ interrupt flag (and hardware pin) every time it fires. These two things are really cool because not only do I have a very fast and very calibrated clock that I can run certain tasks on at a rapid rate, but I can do so within separate tasks which keep the entire program from freezing if set up correctly.

But it required a slight change in thinking. I cannot run intensive processing within the PITR (printf(), etc). I cannot use OSTimeDly(n) (essentially the Sleep() function of uCOS, which is also limited to 20ms minimum) within the PITR. And I cannot utilize critical sections or OSLock() for any more than a few essential operations at a time. Otherwise the watchdog timer of the microcontroller could hang and force it to reboot.

I also have to explicitly declare a task's stack memory on the Netburner and have to give each some priority to make sure that more important tasks continue to run properly.

Thing is, Netburner's documentation on Tasks is quite good once you know where to look.

Embarcadero's documentation is a bit more confusing.

Borland/Embarcadero uses something called the TThread class. I figured multithreading a GUI application would be simple - simply declare a "task," set it up to run by itself using numerous different parameters with timers and what-not. Not according to documentation.

According to documentation you have to set up an entirely separate TThread class and you have to Synchronize objects from the main form in order to change their values. And none of the example programs look very straightforward.

Of course, the only example program I have is some triple-sorting GUI with three threads other than the main one, each sorting a set of data with a different algorithm. Out of the box, it's really cool. You hit the button and watch the three bar graphs simultaneously sort themselves from high values to low values, some taking longer than others. A great example program.

But I don't do any sorting in my programs. All I WANT is to push a button in my GUI and have it attempt to connect to a remote unit for eight seconds without hanging. Meaning, if I know it's not there, I can simply push the disconnect button and cancel it. Or close the GUI completely because I realize I made some mistake in programming.

In short, MY need for multithreading exists on a much simpler level. I want to be able to interrupt something that would otherwise force me to wait, since it emulates the Sleep() function.

Then I figured it out. Threading is still my solution. Only getting it set up in the Embarcadero IDE is much much simpler than the scant documentation and example programs would lead you to believe.

Here's my example program:
1. Create a simple VCL form with a TLabel (or any other object that takes a String input and displays it).

2. Drop a TSpeedButton object, setting its GroupIndex to a positive integer (like 1) and setting its AllowAllUp property to true. Now we have a toggle switch.

3. Create a global bool variable and give it some name like "ThreadExecute."

4. Bind the OnClick event of the TSpeedButton to some function, namely its default "SpeedButton1Click(TObject *Sender)" and tell it to toggle the global boolean set in step 3 based on its Down boolean property:
void __fastcall TForm1::SpeedButton1Click(TObject *Sender) {
  // This ensures that the SpeedButton1->Down status is available globally for other things, such as the other thread.
  ThreadExecute = SpeedButton1->Down;
}

5. Now for the super simple part - File > New > Other > C++ Builder Files > Thread Object. This will create a new unit, a cpp and h file set with the same name that act like a single "unit" (hence the name) within the IDE. The class it automatically creates will be assigned to the TThread class type, just like the main form is assigned to the TForm class type. It will also create two default functions - a constructor for initializing internal variables or what-not when a TThread object of this class is created, and an Execute() function for when the thread is "started."

6. In the TThread::Execute() function, create some iterative process, like adding 1 to a number within an endless while loop:
int n = 0;

void __fastcall ThLooper::Execute() {
  // Note - A while(1) loop, particularly with a Sleep() delay at the end would most certainly freeze the main loop and make it unresponsive to any further user interaction.
  while (1) {
    // Only execute if Toggle button in the Main form is Down.
    if (ThreadExecute) {
      n++;
    }
    Sleep(250);  } // end while
}

7. In the main form, set up the TForm's constructor to initialize the Thread in its own object:
ThLooper *Thread1;
__fastcall TForm1::TForm1(TComponent *Owner) : TForm(Owner) {
  Thread1 = new ThLooper(true); // Note - the CreateSuspended parameter creates the Thread object without starting it automatically.
  Thread1->Start(); // Can also use Resume(). This is needed if the Thread was created with CreateSuspended == true.
}
8. We now will probably need to display the value of n in the main form. However note the comment in the Thread class file - "Methods and properties of objects in VCL can only be used in a method called using Synchronize." This means you must use the Synchronize() function in order to access Form1 objects like the Label we want to update the caption of:
int n = 0;
// Should declare this first. Can be done in the thread header file.
void __fastcall ThLooper::UpdateLabel(void);


void __fastcall ThLooper::Execute() {
  while (1) {
    if (ThreadExecute) {
      n++; // Note - we made this global to access it from a synchronize method call.
      Synchronize(&UpdateLabel);
    }
    Sleep(250);
  } // end while
}

void __fastcall ThLooper::UpdateLabel() {
  Form1->Label1->Caption = (String)"N=" + n;

}

And that's it! That's all I needed to do to set up a thread that runs a constant process that would normally freeze the program outright. I can start the timer, or stop it using the toggle button, but the endless while loop will not freeze any portion of the main thread.

Looking at the documentation, there are also properties for setting a thread's priority to low or high, set it to destroy itself on an exception, and numerous other things.

And my knowledge of Netburner tasks simply verifies what I already know when Embarcadero highlights the following things to be aware of:
1. Too many threads consumes CPU time - limit yourself to 16 threads per CPU processor.
2. If multiple threads access the same resources, they must be synchronized to avoid conflict.
3. Updates to the form must be limited to the main thread. Updating a form element from within a thread should be done via thread synchronization such as TMultiReadExclusiveWriteSynchronizer.

This subject is not as complex as I had first believed!

Official Documentation:

Example.h:

#ifndef ExampleH
#define ExampleH

#include
#include
#include
#include
#include

class TForm1 : public TForm {
__published:    // IDE-managed Components
    TLabel *Label1;
    TSpeedButton *SpeedButton1;
    void __fastcall SpeedButton1Click(TObject *Sender);
    void __fastcall FormShow(TObject *Sender);
private:    // User declarations
public:        // User declarations
    __fastcall TForm1(TComponent* Owner);
};

extern PACKAGE TForm1 *Form1;

#endif


Example.cpp:

#include
#pragma hdrstop

#include "Example.h"
#include "ThLooper.h"

#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
ThLooper *Looper;

__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) {
    Looper = new ThLooper(true); // Initially suspended. We don't want to risk updating VCL components before they are completely created.
}

void __fastcall TForm1::FormShow(TObject *Sender) {
    Looper->Start(); // VCL Form has been fully created. Can now start Looper thread.
}

void __fastcall TForm1::SpeedButton1Click(TObject *Sender) {
    Label1->Caption = (SpeedButton1->Down)?"Running":"Halted";
    Looper->Go = SpeedButton1->Down;
}



ThLooper.h:

#ifndef ThLooperH
#define ThLooperH
#include

class ThLooper : public TThread {
private:
  void __fastcall UpdateLabel(void);
  void __fastcall AnimateStatus(void);
protected:
  void __fastcall Execute();
public:
  __fastcall ThLooper(bool CreateSuspended);
  bool Go;
};

#endif


ThLooper.cpp:

#include
#pragma hdrstop

#include "Example.h" // To update VCL form elements.
#include "ThLooper.h"
#pragma package(smart_init)

// AnimateStatus is a quick little text-based progress bar I made.
char ConnAniCtr = 0;
char ConnAniMax = 4;
char ConnAniDelay = 2;
char ConnAniDelayCtr = 0;
String ConnAni = "";
short N = 0;
void __fastcall ThLooper::AnimateStatus() {
  if (ConnAniDelayCtr < ConnAniDelay)
    ConnAniDelayCtr++;
  else {
    ConnAniDelayCtr = 0;
    if (ConnAniCtr >= ConnAniMax)
      ConnAniCtr = 0;
    else
      ConnAniCtr++;
  } // if

  ConnAni = StringOfChar('.',ConnAniCtr);
  Synchronize(&UpdateLabel); // Synchronously update VCL Form Label.
}

__fastcall ThLooper::ThLooper(bool CreateSuspended) : TThread(CreateSuspended) {
    Go = false; // Do not run initially.
}

void __fastcall ThLooper::Execute() {
  while (1) {
    if (Go) {
        AnimateStatus();
        N++;
    }
    Sleep(250); // Can comment this to see max speed of thread. Synchronize does not significantly pause thread execution.
  }
}

void __fastcall ThLooper::UpdateLabel() {
  Form1->Label1->Caption = (String)"Running" + N + ConnAni;
}

PHP Access Control List

A quick little Access Control List (ACL) snippet I made for PHP/HTML. Enjoy!
<?php

$acl = array(
// Populate with IP/Subnet Mask pairs.
// Any zero bit in the subnet mask acts as a wildcard in the IP address check.
array("192.168.10.24","255.255.255.255"),
);

$acl_allow = false;
for ($i = 0; $i < count($acl); $i++) {
$ip2chk = (ip2long($acl[$i][0]) & ip2long($acl[$i][1]));

if ((ip2long($_SERVER['REMOTE_ADDR']) & $ip2chk) == $ip2chk) {
$acl_allow=true;
}
}

if ($acl_allow) {
// Put all test stuff here!! Only visible to ACL.
phpInfo();
} else {
echo "<a href='http://this-page-intentionally-left-blank.org/whythat.html' target='_blank'>This page intentionally left blank.</a>";
}

?>

Integer Math - Converting to Binary

Yes, I realize this is a beginning digital logic concept, but I need somewhere to write this so I don't forget again.

Unsigned Integer to Binary and Back

You are given a number: 25382
Take the modulo of it to test if it is even or odd: 25382 % 2 = 0 <= ???? ???? ???? ???0
Divide by two to get a new integer: 25382 / 2 = 12691
Take the modulo of the new number to test if it is even or odd: 12691 % 2 = 1 <= ???? ???? ???? ??10
Divide by two to get a new integer: 12691 / 2 = 6345
Take the modulo of the new number to test if it is even or odd: 6345 % 2 = 1 <= ???? ???? ???? ?110
Divide by two to get a new integer: 6345 / 2 = 3172
Take the modulo of the new number to test if it is even or odd: 3172 % 2 = 0 <= ???? ???? ???? 0110
Divide by two to get a new integer: 3172 / 2 = 1586
Take the modulo of the new number to test if it is even or odd: 1586 % 2 = 0 <= ???? ???? ???0 0110
Divide by two to get a new integer: 1586 / 2 = 793
Take the modulo of the new number to test if it is even or odd: 793 % 2 = 1 <= ???? ???? ??10 0110
Divide by two to get a new integer: 793 / 2 = 396
Take the modulo of the new number to test if it is even or odd: 396 % 2 = 0 <= ???? ???? ?010 0110
Divide by two to get a new integer: 396 / 2 = 198
Take the modulo of the new number to test if it is even or odd: 198 % 2 = 0 <= ???? ???? 0010 0110
Divide by two to get a new integer: 198 / 2 = 99
Take the modulo of the new number to test if it is even or odd: 99 % 2 = 1 <= ???? ???1 0010 0110
Divide by two to get a new integer: 99 / 2 = 49
Take the modulo of the new number to test if it is even or odd: 49 % 2 = 1 <= ???? ??11 0010 0110
Divide by two to get a new integer: 49 / 2 = 24
Take the modulo of the new number to test if it is even or odd: 24 % 2 = 0 <= ???? ?011 0010 0110
Divide by two to get a new integer: 24 / 2 = 12
Take the modulo of the new number to test if it is even or odd: 12 % 2 = 0 <= ???? 0011 0010 0110
Divide by two to get a new integer: 12 / 2 = 6
Take the modulo of the new number to test if it is even or odd: 6 % 2 = 0 <= ???0 0011 0010 0110
Divide by two to get a new integer: 6 / 2 = 3
Take the modulo of the new number to test if it is even or odd: 3 % 2 = 1 <= ??10 0011 0010 0110
Divide by two to get a new integer: 3 / 2 = 1
Take the modulo of the new number to test if it is even or odd: 1 % 2 = 1 <= ?110 0011 0010 0110
Divide by two to get a new integer: 1 / 2 = 0
Take the modulo of the new number to test if it is even or odd: 0 % 2 = 0 <= 0110 0011 0010 0110

25382 = 0110 0011 0010 0110

With a bit of programming, this can be highly consolidated:

int decVal = 25382;
char binVal[16];
printf("Integer: = Binary: ");
for (int i = 0; i < 16; i++) {
binVal[i] = (decVal % 2);
decVal /= 2;
printf("%d", binVal[i]);
}


Note however that the binary value's lowest-order bit is expressed on the right-hand side whereas an array's lowest-order element is expressed on the left-hand side. This will result in the direct output of the array being displayed as the reverse of the binary string it represents.

binInt = 0110001100100110;
= B15 B14 B13 ... B2 B1 B0;

binVal[16] = {0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0};
= {I0, I1, I2, ... , I13, I14, I15};

Thus, we will need to parse through the array in reverse order when using its values to either print to the standard output or convert back into the regular integer:

// binVal is calculated from for loop above.
// binVal[16] = {0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0};
int decVal;
printf("Binary String: ");
for (int i = 0; i < 16; i++) {
if (binVal[16-(i+1)] == 1) {
decVal += pow(2,i);
}
printf("%d",binVal[16-(i+1)]);
}
printf(" = Integer: %d",decVal);

My New C++ Endeavors

Awesome news! I've been asked at work to start doing a bit of GUI development for our NASA projects. They've said that all of their stuff is developed using Borland C++. Now with my PennMUSH development experience, I've taught myself a bit of C++ (though my PennMUSH development experience has been mostly debugging and making sure it compiles under Windows, so it isn't too in depth).

One of the big hangups I've always had with C++ is the pointer and reference concepts. And I think I know why now. It's because immediately after teaching us pointers in college, our teacher jumped right into recursion. And it was hard enough grasping pointers without sending my mind into an out-of-control fractal spiral.

However, reading C++ All-in-One for Dummies (the big 7-volume one that I got at Barnes and Noble for a surprising $35), the entire concept of pointers suddenly made sense to me!!

Here it is in a nutshell:
Variable has a value.
Pointer has a value also. Its value is actually a hex word.
Pointer's value, that hex word, points at the memory location of the variable.
We can access the variable's value via the pointer, which expands our capabilities of manipulating it.

And in short:
var = var value
&var = var location
ptr = var location
*ptr = var value

As you can see, all we REALLY care about in the end is what or where the variable is.

Then we get into heap variables where we don't even care what the variable is called! We only work with it via pointers!

Then apparently C++ has this new thing that wasn't in C called Referencing. This is, at least from what I know, a shorthand way of passing actual variables in and out of functions.

Functions normally just pass variable values in and out:
int manipulate(int input) {
    bigValue = input + 5;
    return bigValue;
}
int main() {
    smallValue = 5;
    cout << smallValue << endl; // Will return 5.
    nextValue = manipulate(smallValue);
    cout << nextValue << endl; // Will return 10 (Function added 5 to 5).
    cout << smallValue << endl; // Will return 5. Actual variable passed in is unchanged, because we really only passed in its VALUE.
}
With references, we can actually change the VARIABLE we pass in:

void manipulate(int &smallValue) {
    smallValue += 5;
    // We aren't returning anything! We don't have to because we're not manipulating an internal function variable anymore.
}
int main()  {
    smallValue = 5;
    cout << smallValue << endl; // Will return 5
    manipulate(smallValue);
    cout << smallValue << endl; // Will return 10. The REFERENCE to the variable was passed in, rather than the value, so the function worked on the variable we were referencing rather than one it created itself.
}
So I'm not too adept on the actual advantages to all this, but then again, I don't need to know its advantages right away in order to get excited about understanding it!


PLC and Hardware Control


Aha! Thank you, Dad for my new Arduino hardware controller (and the partially-outfitted Craftsman toolbox to make my life simpler when building stuff)! Easy easy to program, and easier to wire up!

There was a time a few years ago that we were playing with a Seimen's PLC at my old job, and the process to program it via their development package was more confusing than anything. I've seen Lego Mindstorms being programmed: VERY complicated. Arduino has taken these shortcomings of other PLCs and simplified -everything- to make these things easy to understand. All you REALLY need to know is basic electronics and basic programming structure (preferrably in C) and you too can program up one of these things.

The interface is amazingly simple. No frills, no complicated menus, no toolboxes or hundreds of buttons to hunt for in some confusing tree structure of some far-off galaxy of a development suite. Open the development software and you get a single window with a single text box and a base menu. Open one of the examples from this window (simply File>Examples), and you get another window with some predefined text in the textbox. Simple.

So really, the only things keeping me from creating some amazing electronic system now is 1) lack of resources, and 2) a blockage in my own mental creativity.

If I wanted to create a mobile robotic system, I would need motors and drives for them: something which I simply do not have at the moment. But I don't know if I WANT to make a mobile robotic system. Or a light fader. Or a garage door opener (actually, NO I do NOT want to make this. It's been overdone), or something else. So for now I just have to settle for IBBLs in random or pseudo-random or extremely predictable patterns. Fun stuff though! More to come, maybe!



I want ideas. Perhaps if I open a poll, or something similar, I can get your feedback (and mine as well). I have about 25 minutes, so let's see if I can get something set up in that time...

Edit: See big fat link above (or QR below).


Ruby on Rails for Windows

It was only recently that I FINALLY figured out just what Ruby on Rails is. From most of the posts I saw all I could make of it was "it's the thing that runs Twitter." Okay..so what's it DO?

Well as it turns out, from what I can make of it, Rails is a new web framework designed to streamline web design...BETTER than PHP/ASP/Javscript. So basically it's a new kind of webserver.

Well I decided that I wanted to try it out. Unfortunately, I don't run on Linux, and every time I do I get very frustrated. So I decided to try and get it going on Windows (XP to be exact). And I did it! Of course, I had to use an online tutorial (which is fairly outdated by about 2 years) located at All About Ruby on Wordpress.

And with a few modifications and updates to the main system, I was able to get it to work as is right now!

And this is as follows:

Install MySQL (or any other SQL engine, but I already have MySQL installed, so that's nice.)

Install Ruby (I had an old version 1.8.7, so I upgraded to 1.9.3p0. The tutorial says you'd also need to install gems, but I didn't have to. 1.9.x came with it pre-loaded. The installer for Windows is located at RubyInstaller. I installed to C:\Ruby\ (they recommended C:\Ruby193\, but I might want to upgrade. And what's next? Ruby194? Ruby200?).

I also made sure I included C:\Ruby\ in the windows path, and associated *.rb files. I also installed the TCL/TK addon, which I doubt I'll ever need, but hey, it's America where more is better!

One thing I also noticed later on in the installation was that I needed the optional DevKit located on the same page at RubyInstaller. At the time of this writing, the file is called DevKit-tdm-32-4.5.2-20110712-1620-sfx.exe. Otherwise the "gem" (a friendly name for ruby add-on methods).

The GitHub tutorial states that DevKit was written under MinGW. Now I'm not too happy with Linux frameworks in Windows (MinGW, Cygwin, etc), so I was a little worried that I'd have to install the actual framework. Not so! If you look at the GitHub tutorial, you'll see it has installing boiled down into three nice easy steps.

Unzip (from 7zip format) into a nice friendly Ruby-Dev folder (I chose C:\ruby-dev\) and navigate in via cmd window. By the way, I've learned it's never good to run ruby scripts from Windows Explorer. If it fails, the resulting command window closes automatically and you never see what's going on, so it's better to Start/Run/cmd.exe and navigate in via the DOS window.

Once in the Ruby-Dev directory, you could run
ruby dk.rb.

This by itself will just display a help screen with what you're actually supposed to do.


ruby dk.rb init
This generates a config.yml (a configuration file which in this case just tells the installer where the actual Ruby installation is located). You can open this config.yml in a text editor and see what's going on. There should be an entry near the bottom that says something like
- c:/ruby
or some such. If this line doesn't match your Ruby installation, change it so that it does. Remember you only want to go as deep as Ruby's top-level directory, not "bin" or anything deeper.

Next you run:
ruby dk.rb review
and get to see exactly what it's going to do based on your config.yml. It's not too detailed, so you could just skip this step and go straight to:
ruby dk.rb install


It worked for me, even when I did the confirmations listed in the Devkit tutorial on Github, but I won't tell you how to do that here. You can just read it for yourself (see the link above).

With this done, I could now begin the Rails installation. Remember rails is simply a new way of web developing. Basically, underneath all of the sparkles, it's nothing more than a webserver. Sure a webserver with a completely new way of writing the actual pages, but a webserver nonetheless.

According to Rails On Windows, you can now install the Rails gem. Remember gems are just add-ons for Ruby, much like you can install Android development kits to implement for the Java language. Except gems are just little default programs that can be called from any other program you write yourself to add functionality.

So anyway, install the Rails gem with dependencies included by running from the command window:
gem install rails --include-dependencies


If all goes well, the script should download all of the gems required to run rails out of the box, plus all of the documentation (ri and rDoc).

Once this is done, you'll be able to create a new directory for your rails project(s) (I put mine in C:\Rails), navigate there and run:
rails firstproject


This will set your project up and hopefully not stall out and crash when it tries to install JSON support (it did when I tried, but that's why I set up the DevKit detailed before, and after I did, it worked fine).

Once it's completed and you have received the 'finished successfully' message, you can now navigate into that project folder (cd firstproject), and run:
ruby ./script/rails server


Rails will now start up and you should see a few messages such as:

=> Booting WEBrick
=> Rails 3.1.1 application starting in development on http://0.0.0.0:3000
=>Call with -d to detach
=>Ctrl-C to shut down server
[2011-11-12 13:17:03] INFO  WEBrick 1.3.1
[2011-11-12 13:17:03] INFO  ruby 1.9.3 (2011-10-30) [i386-mingw32]
[2011-11-12 13:17:03] INFO  WEBrick::HTTPServer#start: pid=952 port=3000
It may take a while for all of these messages to appear, but once they have, you can now go into your browser and navigate to http://localhost:3000 and HOPEFULLY see a 'Congratulations and welcome to Ruby on Rails' or some such title screen.

That's the extent of THIS tutorial. I still have yet to figure out how to set up the database and tables and make it all come together that way, but at least I got the server up and running and displaying something nice in my browser!

Geotag Hack for Garmin Nüvi/Panoramio Photos

How to manually geotag photos for the Garmin Nüvi 1490
Does NOT require the Garmin Connect software.


Recently (yesterday) I was nominated to read up on and figure out the Garmin Nüvi 1490 GPS unit for a friend of the family. Well, looking through the features, I found that photos can be loaded into the unit and displayed in various locations, such as the bootup screen, a simple slideshow, or even as a navigate-to-photo feature!

Now, I assumed that somehow the unit would read out Geotagging meta data from any old photo you might tag in something like Picasa and copy to the unit. But this is not so. Actually, Garmin has signed a contract of some sort with Panoramio, an online photo-sharing site specifically for photos of general public areas, usually landscapes, historical photos, or any other pictures based mainly on a location. By connecting the Nuvi to the computer, one can navigate to http://connect.garmin.com/photos, browse the map for specific Panoramio photo, which, if they have geotag information loaded, will show up on the map, and add it to a separate dock area, which, by using the Garmin Connect software, can be downloaded directly to the GPS unit.

All well and good, except that in the last month and a half, I've uploaded about 20+ photos to Panoramio and NONE of them are showing up on the Garmin Connect map, though they specifically said in the Panoramio forums that they would after some time. And to top it off, a photo that my dad posted about four years ago is also not appearing! Not very on top of this little feature, are we?

Besides that, all of the other threads on Panoramio boiled down to "downloading Panoramio photos to your computer is illegal" and NONE of them gave any helpful information.

So, I decided to do some research and figure out just exactly how the Garmin Connect tool synchronized with the photos feature. When I plugged the unit into the computer via USB, two new drives were mounted: the Garmin Nuvi drive, which is the image of the internal memory, and a Removable Disk, which was accessing the Micro-SD expansion port.

Navigating into the Garmin Nuvi drive (on my machine, I:), there are a number of files and folders:
.System
Garmin
GPX
help
JPEG
keyboards
Reports
Screenshot
Text
Vehicle
Voice
.Volume.icns
autorun.inf
nuvi_drive.ico
Navigating into the GPX folder, I find three files:
Current.gpx
Panoramio_20110810T103416.gpx
Position.gpx
From my previous research using our own GPS unit, the GPSMAP 60CSx, I know that GPX files are an XML-based file that can be opened in any text editor, and record specific gps coordinates along with names of the locations, timestamps and sources. Knowing that the Garmin stored everything in GPX made decoding it so much easier! With this knowledge, I was able to determine that GPX/Current.gpx is the database that stored saved searches, tracks and tagged photos downloaded from Panoramio, GPX/Panoramio_20110810T103416.gpx is the list of geotagged photos downloaded from Garmin Connect/Panoramio, and GPX/Position.gpx is probably the unit's current position.

Assuming that the Current.gpx file is updated regularly based on what the the unit is detecting, I decided to decode the Panoramio_20110810T103416.gpx file. And it worked! The conclusion I came to was the following: photos downloaded from GC/Panoramio are not tagged in the unit using JPEG metadata, and the unit can't detect JPG location metatags. Instead, Panoramio also sends location data alongside the photos that the unit downloads, and injects this information into the Panoramio_*.gpx file. THEN when the photos are opened in the viewer, the proper location-based tools are activated.

Here is a waypoint for a specific Panoramio photo:

<wpt lat="40.615972" lon="-105.171969">
<name>Northwest of Fort Collins</name>
<src>Panoramio <link href="Garmin/JPEG/Panoramio_11075531.jpg">
<type>image/jpeg </link>
<link href="http://www.panoramio.com/user/965071">
<text>WildernessShots.com</text>
<type>text/html</type>
</link>
<link href="http://www.panoramio.com/photo/11075531">
<type>text/html</type>
</link>
<sym>Scenic Area</sym>
<extensions>
<gpxx:WaypointExtension xmlns:gpxx="http://www.garmin.com/xmlschemas/GpxExtensions/v3">
<gpxx:Categories>
<gpxx:Category>Photos</gpxx:Category>
</gpxx:Categories>
</gpxx:WaypointExtension>
</extensions>
</wpt>
With this information, I think it would be fairly self-explanatory to edit this file. I have yet to actually try it out, but when I figure it out, I'll get back with you!
------------
SUCCESS!!! I simply added a new <wpt> ... </wpt> section to the panoramio_20110810T103416.gpx file and when I rebooted the unit (unplugged it from the computer), the photo appeared in the list AND had the location tools in the lower corner, along with my username and all that!! WONDERFUL!

And just as I suspected, Current.gpx was also updated to reflect these changes, even though I didn't do a thing to them.

So all that needs to be done to add photos to your Garmin, and tag them without worrying about the Garmin Connect/Panoramio tool, simply copy the photos into one of the locations where pictures are kept on the unit. The tool downloads them to /Garmin/JPEG/, but I'm sure /JPEG/ will do just fine (/JPEG/ is where all the sample photos are kept).

Then edit the /GPX/Panoramio_<datecode>.gpx file and add a new waypoint using the <wpt> ... </wpt> tags as shown above. Or if you have a secondary program to save GPX files, I'm sure there is a way to just fix this up to save in this format. Also, the datecode in the filename is in the following format: yyyymmddThhmmss, so in my case, it was 20110810T103416. Just to keep up with the consistency, I also changed the filename to Panoramio_20110810T173100.gpx.

So in the end, here is the waypoint I manually and successfully added, along with the required XML and GPX definition tags:


<?xml version="1.0" encoding="UTF-8"?>
<gpx xmlns="http://www.topografix.com/GPX/1/1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd" version="1.1" creator="http://connect.garmin.com/">
<wpt lat="40.631839" lon="-105.143142">
<name>Sunrise Over the Neighborhood</name>
<src>Panoramio</src>
<link href="Garmin/JPEG/56784165.jpg">
<type>image/jpeg</type>
</link>
<link href="http://www.panoramio.com/user/5873911">
<text>Daniel P</text>
<type>text/html</type>
</link>
<link href="http://www.panoramio.com/photo/56784165">
<type>text/html</type>
</link>
<sym>Scenic Area</sym>
<extensions>
<gpxx:WaypointExtension xmlns:gpxx="http://www.garmin.com/xmlschemas/GpxExtensions/v3">
<gpxx:Categories>
<gpxx:Category>Photos</gpxx:Category>
</gpxx:Categories>
</gpxx:WaypointExtension>
</extensions>
</wpt>
</gpx>
I'm sure if you wanted to add non-panoramio files, it would be just as easy. Just change/remove the few <link> tags to reflect something else and you'd be good to go!

Now to write a program to do this for me...


JFrame

A JFrame is a little more than just a regular java app. You must extend and import a bunch of graphics classes included with JDK. To do this you need the following:

import java.io.*;
import java.net.*;
import java.awt.*;
import java.util.StringTokenizer;
import javax.swing.*;
import javax.swing.border.*;

public class ClassName extends JFrame {

static Container c;
static JFrame x;
static Int Config_Width;
static Int Config_Height;
static Int WindowX;
static Int WindowY;

public static void main(String[] args) throws IOException {

x = new ClassName();
x.setSize(config_width, config_height);
x.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
x.setResizable(false);
x.setAlwaysOnTop(true);
x.setVisible(true);
x.setLocation(windowX, windowY);
}
}


Here is a small list of JFrame variables and what they do:


  • SetSize(config_width, config_height); - changes the size of the window
  • setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - I believe this adds the close button on the upper right corner.
  • setResizeable(true/false); - can you drag the window edges to resize it?
  • setAlwaysOnTop(true/false); - basically a "pin window" command, making it on top of everything else, even if it is out of focus.
  • setVisible(true/false); - is the window visible? It can exist, but it doesn't have to actually work.
  • setLocation(windowX, windowY); - sets the initial position of the upper left-hand corner of the window with respect to the upper left-hand corner of the monitor (starting at (0,0)).

Programming

So I got a JFrame app the other day for a plugin on a program. I found out how to change variable values in a text file without a recompile of the java class. Here it is (I did not include the preliminary java declarations/import classes, but they must also be included as usual).




The var declare portion of app.java file

Static int varName = Integer.parseInt(readData("config.txt", "WindowWidth"));



The config.txt file

WindowWidth = 205


After compiling the java file (cmd "javac app.java"), you will be able to change the initial value of varName by changing the value of WindowWidth in the text file. It works great!!