Subscribe via RSS

Lighting a Japanese Temple

Actual Temple Kit

I'd decided it was time to light the temple after building the Torii for the entrance. This temple was the Tomytec Japanese Temple A (Main Building) and is still available for purchase from most Japanese online hobby retailers.

I've slapped LEDs in buildings before, but this time I also wanted to add lanterns to the front of the shop. I'd made the lanterns before, as in my previous attempts of creating the Torii, but I was to make a few changes this time as I wasn't totally impressed with the previous outcome.

Creating the lanterns

There was a slight change this time to creating the lanterns... instead of cutting them and sliding them over the LEDs, I shaved them down to fit and inserted them into the center of the tubing. This all worked well, but you must be careful when shaving down the LEDs as you can destroy them quite easily. To shave the LEDs, I held them in pliers in one hand and filed away with my pocket knife. It was pretty obvious to feel when you were no longer filing away at plastic and, unfortunately, this was usually the demise of the LED.

Shaving down LEDs LED inside plastic tubing

Mounting the lanterns

I used the same copper winding wire that I always do and bent it into a rectangular shape to fit the roof of the temple. I then started soldering the lanterns in place.

Lantern on wire Using wire to frame lanterns in place

I then pulled out the trusty Selleys Aquadere and, using random aligator clips found on the bench, glued the lanterns in place.

Gluing down lanterns in place Aligator clips do wonders

I also put two standard 3mm white LEDs in the center of the ceiling for building lights.

Central building lights

The finished product

After the glue had dried, I tested all the LEDs and found that I'd broken the front-left lantern. This was 24hours after starting the project and frustrating. I quickly removed it from the temple and filed another LED down. I left it dry again, overnight, after testing, gluing and testing again.

Finally, yesterday, I was able to hook it up to my Arduino LED Controller. It worked perfectly and I took the opportunity to test my night-time photography skills.

Finished job View past the city And the Torii
A different view Night time Top Platform

Now to settle the landscape around it.

18Jan/110

An attempt to simulate Acceleration and Braking

In my previous post, I'd managed to get my Densha-De-Go Dreamcast controller hooked up to my Arduino Mega. Now although this now meant that I had a great way to control my model railroad, it also meant I had to work out how to code a throttle and a brake lever.

The rules

After a few hours of plotting, I had decided on the following system. It involves a 6-position throttle and an 11-position brake. Each 'position' is to have a 'max speed' and 'speed adjust' associated with it.

Notes

  • It is to be assumed that if the brake is on, the throttle is automatically disabled
  • MAX is 255 on the PWM Throttle (or max voltage from supply)
  • At Throttle 0, the train is neither powering nor braking; we will simply slowly decrement the speed
  • There is no feedback to know how fast the train is currently travelling
  • There are multiple emergency brake points on the throttle, but we don't care about them.

The next table shows my acceleration and braking deltas. This will be a simple addition and subtraction on the current speed.

Lever position Max Speed Speed Adjustment
Emergency Full Instant Stop
Emergency 5 -50
Emergency 4 -30
Emergency 3 -25
Emergency 2 -20
Emergency 1 -10
Brake 9 -2.4
Brake 8 -1.8
Brake 7 -1.2
Brake 6 -0.8
Brake 5 -0.4
Brake 4 -0.2
Brake 3 -0.1
Brake 2 -0.05
Brake 1 -0.025
Throttle 0 0 0.00
Throttle 1 55 +0.25
Throttle 2 75 +0.5
Throttle 3 90 +1
Throttle 4 100 +1.75
Throttle 5 120 +2.5

And now a better way to represent it.

brake-accel-graph

The code

The table above translates to code quite easily... the goal is to have the lever position values coded into an array and then just select the correct entry. Once determined, the main code loop can then determine how to adjust the current voltage output to the rails.

const int throttle_positions = 21;
const int throttle_absolute_maximum_speed = 255;
const int throttle_minimum_speed = 20;
int current_throttle_position = -1; // 0 is EM FULL. Lever must be moved to EM FULL to begin.
float current_speed = 0;
float target_speed = 0;
int throttle_max_speed[throttle_positions] =  {
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, //brake positions and Throttle 0
	0, 55, 75, 90, 100, 120
};
float throttle_delta[throttle_positions] = 	{
	0.025, 0.05, 0.1, 0.2, 0.4, 0.8, 1.2, 1.8, 2.4, 10, 20, 25, 30, 50, 9999, //brake
	0.00 /*coast*/, 0.25, 0.5, 1, 1.75, 2.5
};

We then need to determine the current throttle position. We will make it that, at the start of code execution, the train should not move until the throttle has been reset to EM Full and the throttle at '0'.

#define BRAKE_MASK 0xf0
#define BRAKE_SHIFT 4
#define ACCEL_MASK 0x07
void read_throttle_position() {
	int accel = packet.data[6] & ACCEL_MASK;
	int brake = (int)((packet.data[7] & BRAKE_MASK) >> BRAKE_SHIFT);

	if (current_throttle_position == -1) {
		//check that we have EM FULL and Neutral
		if (brake  == 15 && accel  == 1) {
			//set the initial '0' (EM FULL) position.
			current_throttle_position = 0;
			lcd.clear();
		}
	} else {
		if (brake != 1) { //1 == "BRAKE 1", if it's higher, we're braking.
			if (brake > 0) current_throttle_position = brake;
		} else { //BRAKE == 1 and then we check the throttle
			//we're accelerating.
			if ((accel + 15) < 22) current_throttle_position = accel + 15;
		}
	}
}

And now the main game loop needs to determine the current lever locations and then choose the appropriate action:

void update_speed() {
		digitalWrite(13, LOW);
	//make sure we are allowed to go.
	if (current_throttle_position >= 0) {
		if (current_speed > throttle_max_speed[current_throttle_position - 1]) {
			current_speed -= throttle_delta[current_throttle_position - 1];

			//braking... don't go negative.
			if (current_speed < throttle_minimum_speed)
				current_speed = 0; 

		} else if (current_speed < throttle_max_speed[current_throttle_position - 1]) {

			//accelerating, so start from minimum speed.
			if (current_speed < throttle_minimum_speed)
				current_speed = throttle_minimum_speed;

			current_speed += throttle_delta[current_throttle_position - 1];

			//don't go faster than current throttle max setting.
			if (current_speed > throttle_max_speed[current_throttle_position - 1])
				current_speed = throttle_max_speed[current_throttle_position - 1];
		}

		//set light if we have met max speed for throttle.
		if (current_speed == throttle_max_speed[current_throttle_position - 1])
			digitalWrite(13, HIGH);

		//output speed to railway.
		analogWrite(7, current_speed);
	} else {
		//flash the LED to alert user to reset controls.
		delay(200); //delay a little to flash the LED
		digitalWrite(13, HIGH);
		delay(200);
	}
}

There's also some code in the main loop to update the 16x2 LCD I've hooked up. Since we need to reset the controller when we start, it'll tell you to do so and afterwards will tell you the current throttle/brake position, current speed and speed delta.

Download the source code here.
Note that this does not include the full arduino-maple code. Download that here. You will also need the LiquidCrystal library from the Arduino site.

Action shots

Shown is the controller in certain positions. Note that the 'Throttle' positions may show a negative speed delta; this just means that they are no longer accelerating.

DSC03765 DSC03766 DSC03770

DSC03775 DSC03776

17Jan/114

Arduino + Dreamcast Densha-De-Go Controller

I've had Densha-De-Go and the controller for the Dreamcast for a while now... I'd even invested a few hours in playing the actual game, but the accuracy required is crazy. Supposedly in Japan it's that realistic that even real train drivers have a hard time getting it spot on :)

Anyway, I'd wanted to get this going for train control for a while after managing to make a Wii Nunchuck control my trains.

DSC03710 DSC03712 DSC03715
DSC03707 DSC03698

You can find more information on the controller at SEGA Retro, SEGAGAGA Domain, Wikipedia, Play Asia and genki Video Games.

Previous attempts

Quite a few months ago I spent a few hours with my Densha-De-Go controller and my Arduino Mega in an attempt to get them to communicate. The goal was to be able to read the controller and then use it to control my model railroad. Unfortunately, I was doing this blind, running off information from Marcus Comstedt's 'Dreamcast Programming' site; specifically his breakdown of the Maple Bus Protocol.

I ended up with nothing working and sent out a few pleas for help on the Arduino forum and to Marcus himself. I received information regarding the fact that it should work (the Arduino had the horsepower), but I would have to ensure the timing was intricate and that the Arduino was always ready to receive data.

I then posted to the Arduino Forum for help. A few months later WzDD came to my rescue with word that he had successfully made the Arduino communicate with a Dreamcast Controller. I hadn't had much time up until now to work on this, and WzDD hadn't done any further work on it, so I took it that he just wanted to prove the concept and that I'd have to get off my backside to make things progress further. In the end, I did want to get my controller functioning.

I had previously had everything I needed to test this again, so I gave it another go. My previous setup was a cut-in-half Dreamcast controller extension cable and my Densha-De-Go controller. The extension cable meant I didn't have to hack around with the actual controller or port imitation.

Getting set up...

Due to the requirement to use assembler for the actual low-level controller interactions, we cannot use the Arduino IDE and must use WinAVR (or just avrdude on Linux.) Download this from here (select the latest version) and install to the default directory.

You then need to download the arduino-maple source code and extract somewhere locally. I've put the folder on C:\arduino-maple\.
Open Programmers Notepad [WinAVR] from the start menu and then open the arduino-maple.cpp file from where it has been extracted. (C:\arduino-maple\arduino-maple.cpp in my case.)
If all is installed correctly, then you should be able to choose Tools - [WinAVR] Make All and have the following output in the output window:

> "make.exe" all
avr-gcc -Wall -g2 -gstabs -Os -fpack-struct -fshort-enums -ffunction-sections -fdata-sections -ffreestanding -funsigned-char -funsigned-bitfields -mmcu=atmega328p -DF_CPU=16000000UL -I./arduino -c arduino/pins_arduino.c -o build/arduino/pins_arduino.o
In file included from arduino/wiring_private.h:30,
                 from arduino/pins_arduino.c:26:
c:/winavr-20100110/lib/gcc/../../avr/include/avr/delay.h:36:2: warning: #warning "This file has been moved to <util/delay.h>."
...
Lots of warnings, no errors...
...
avr-g++ -Os -Wl,-gc-sections -mmcu=atmega328p  build/arduino/pins_arduino.o build/arduino/WInterrupts.o build/arduino/wiring.o build/arduino/wiring_analog.o build/arduino/wiring_digital.o build/arduino/wiring_pulse.o build/arduino/wiring_shift.o build/./arduino-maple.o build/arduino/HardwareSerial.o build/arduino/Print.o build/arduino/Tone.o build/arduino/WMath.o build/arduino/WString.o build/./libmaple.o -o app.elf
avr-objcopy -R .eeprom -O ihex app.elf  app.hex
avr-size --format=avr --mcu=atmega328p app.elf
AVR Memory Usage
----------------
Device: atmega328p

Program:    3380 bytes (10.3% Full)
(.text + .data + .bootloader)

Data:        796 bytes (38.9% Full)
(.data + .bss + .noinit)

> Process Exit Code: 0
> Time Taken: 00:02

Now, as you can see above... that's not my Arduino! I have an Arduino Mega and therefore we need to adjust the Makefile correctly for my setup. Here you'll need to know the CPU code, interface type and the port number. Also make sure you change the upload task to program so that we can use the menu items in WinAVR.

# Makefile for building small AVR executables, supports C and C++ code
# Author: Kiril Zyapkov
# Hacked up by nfd

SOURCE_DIRS = . arduino
INCLUDE_DIRS = arduino
MMCU = atmega1280
F_CPU = 16000000UL
SRC_ROOT = .
BUILD_DIR = build

CFLAGS = -Wall -g2 -gstabs -Os -fpack-struct -fshort-enums -ffunction-sections \
 -fdata-sections -ffreestanding -funsigned-char -funsigned-bitfields \
 -mmcu=$(MMCU) -DF_CPU=$(F_CPU) $(INCLUDE_DIRS:%=-I$(SRC_ROOT)/%)

CXXFLAGS = -Wall -g2 -gstabs -Os -fpack-struct -fshort-enums -ffunction-sections \
 -fdata-sections -ffreestanding -funsigned-char -funsigned-bitfields \
 -fno-exceptions -mmcu=$(MMCU) -DF_CPU=$(F_CPU) $(INCLUDE_DIRS:%=-I$(SRC_ROOT)/%)

LDFLAGS = -Os -Wl,-gc-sections -mmcu=$(MMCU) #-Wl,--relax

TARGET = $(notdir $(realpath $(SRC_ROOT)))
CC = avr-gcc
CXX = avr-g++
OBJCOPY = avr-objcopy
OBJDUMP = avr-objdump
AR  = avr-ar
SIZE = avr-size

SRC = $(wildcard $(SOURCE_DIRS:%=$(SRC_ROOT)/%/*.c))

CXXSRC = $(wildcard $(SOURCE_DIRS:%=$(SRC_ROOT)/%/*.cpp))

ASMSRC = $(wildcard $(SOURCE_DIRS:%=$(SRC_ROOT)/%/*.S))

OBJ = $(SRC:$(SRC_ROOT)/%.c=$(BUILD_DIR)/%.o) $(CXXSRC:$(SRC_ROOT)/%.cpp=$(BUILD_DIR)/%.o) $(ASMSRC:$(SRC_ROOT)/%.S=$(BUILD_DIR)/%.o)

DEPS = $(OBJ:%.o=%.d)

$(BUILD_DIR)/%.o: $(SRC_ROOT)/%.c
	$(CC) $(CFLAGS) -c $< -o $@

$(BUILD_DIR)/%.o: $(SRC_ROOT)/%.S
	$(CC) $(CFLAGS) -c $< -o $@

$(BUILD_DIR)/%.o: $(SRC_ROOT)/%.cpp
	$(CXX) $(CXXFLAGS) -c $< -o $@

all: app.hex printsize

#$(TARGET).a: $(OBJ)
#	$(AR) rcs $(TARGET).a $?

app.elf: $(OBJ)
	$(CXX) $(LDFLAGS) $(OBJ) -o $@

$(BUILD_DIR)/%.d: $(SRC_ROOT)/%.c
	mkdir -p $(dir $@)
	$(CC) $(CFLAGS) -MM -MF $@ $<

$(BUILD_DIR)/%.d: $(SRC_ROOT)/%.cpp
	mkdir -p $(dir $@)
	$(CXX) $(CXXFLAGS) -MM -MF $@ $<

#$(TARGET).elf: $(TARGET).a
#	$(CXX) $(LDFLAGS) $< -o $@

app.hex: app.elf
	$(OBJCOPY) -R .eeprom -O ihex $<  $@

clean:
	echo $(RM) $(DEPS) $(OBJ) $(TARGET).*

printsize:
	avr-size --format=avr --mcu=$(MMCU) app.elf

# Programming support using avrdude. Settings and variables.
PORT = /dev/tty.usbserial-A700ekGi
#PORT = /dev/ttyUSB0
AVRDUDE_PORT = com3
AVRDUDE_WRITE_FLASH = -U flash:w:app.hex
MCU = atmega1280
AVRDUDE_PROGRAMMER = stk500v1
#AVRDUDE_FLAGS = -V -F -C \app\arduino-0021\hardware\tools\avr\etc\avrdude.conf
AVRDUDE_FLAGS = -V -F \
-p $(MCU) -P $(AVRDUDE_PORT) -c $(AVRDUDE_PROGRAMMER) \
-b $(UPLOAD_RATE)
UPLOAD_RATE = 57600
#
# Program the device.
INSTALL_DIR = \app\arduino-0021
AVRDUDE = avrdude
program: app.hex
	#python pulsedtr.py $(PORT)
	$(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_WRITE_FLASH)

The first line in the program target (Line 93) 'pushes' the reset button on the Arduino. As we don't have Python installed we have to do this manually. So, build the source code via the [WinAVR] Make All and ensure there are no errors. Now press the 'reset' button on the Arduino and then quickly choose Tools - [WinAVR] Program. If all goes correctly then you'll see the code uploading to your Arduino:

#python pulsedtr.py /dev/tty.usbserial-A700ekGi
avrdude -V -F -p atmega1280 -P com3 -c stk500v1 -b 57600 -U flash:w:app.hex
avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.05s

avrdude: Device signature = 0x1e9703
avrdude: NOTE: FLASH memory has been specified, an erase cycle will be performed
         To disable this feature, specify the -D option.
avrdude: erasing chip
avrdude: reading input file "app.hex"
avrdude: input file app.hex auto detected as Intel Hex
avrdude: writing flash (3508 bytes):

Writing | ################################################## | 100% 1.09s

avrdude: 3508 bytes of flash written
avrdude done.  Thank you.

> Process Exit Code: 0
> Time Taken: 00:03

And that's it, our the compiled code for the arduino-maple project is now on the Arduino and running!

Using the Python code on Windows

I lied, I said I didn't have Python installed... but I ended up installing it anyway. The job to convert the Python code to C# was going to take too long and, to prevent a debugging nightmare, I decided I would get the known-to-work Python code going under Windows first. So, I installed Python from the official website and then started learning :)

Note that I downloaded and installed Python Version 2.6.6 as this was the version that would have been available when arduino-maple was created.

After selecting 'IDLE' from the start menu, I was presented with a light-weight GUI. I opened up the 'maple.py' that's included with the arduino-maple code and attempted to compile it.

I had problems at the start, as in the included Makefile there are two declarations of CPU type. Initially I hadn't set these both correctly and the Arduino includes were not correctly compiled... make sure you carry out a proper clean whenever changing code; this includes manually deleting all of the .o files.

Once this was sorted, it all finally worked... sort of?

connecting to COM3:
connected
SENT: 0000200121
No device found at address:
0x20
SENT: 0000010100
1c20000501000000ff0f3f000000000000000000415400ff204f544920313030746e6f436c6c6f7
2202072652020202020200024191bdc94191958dd481e508815481c9bdc99591b98da5308195
cdb995bdc91881154c8c9b40481051d491551394d25494130b14d1480b911508080808007d00371 117
Command 5 sender 0 recipient 20 length 1c
Device information:
Functions  : CONTROLLER
Periph 1   : 0x3f0fff
Periph 2   : 0x0
Periph 3   : 0x0
Name       : ..TAITO 001 Controller      $.
License    : .....X...P.H..H..Y...S....\....[..T.....Q.I.I%M9M.0A.......P
Power      : 53251
Power max  : 32775
Play with the controller. Hit ctrl-c when done.
SENT: 010020090100000029
SENT: 010020090100000029
1111101010011111
SENT: 010020090100000029
1111101010011111
SENT: 010020090100000029
1111101010011111
SENT: 010020090100000029
1111101010011111

Timing issues

As I continued to run the code, I would get random results as to the 'Name', 'License' and 'Power' fields... well, it was obvious in the text fields that there were problems, but I had no idea that what the 'Power' fields were meant to read. Either way, this indicated a timing issue somewhere and I guessed that life was about to get difficult. Note that I had always been running the controller at 5v, as that's what WzDD's post said the blue wire should be connected to, but it turns out that the device should have actually been running on 3.3v.

Unfortunately, setting the controller on to 3.3v didn't change anything... the responses were still mildly random. The buttons would be sent though correctly (apart from the B button) but the initial device description would come through jumbled. I took a few samples of the data and realised that the data would always have a minimal length. To me this meant that we weren't missing bytes/bits, but actually reading too many. The analysis showed that there were similar chunks all the way through, but at certain intervals there'd be extra/changed characters. Based on Marcus Comstedt's Maple Bus information, I guessed that we were re-reading bits too quickly and needed to slow down a little... This would mean slowing down the pin reading in WzDD's arduino-maple assembler code.

In the maple_rx_raw: function down near _rx_loop:, the code iterates through the pins hoping to read the data. It initially checks the state of the pins to sense when the controller is about to send data, but this did not have the final check for the second pin going low.

4:	IN rport2, _SFR_IO_ADDR(PINB)
	BST rport2, bitmaple5			; maple5
	BRTS 4b							; must be low now

The above code was added around line 305 to ensure this check was in. I then also 'spaced' out the store/read calls by placing a delay in. WzDD had already written the delayquarter macro and I simply re-used this.

	...
	_rx_store rport bitmaple5 5
	_rx_read bitmaple5
	delayquarter
	_rx_store rport bitmaple1 4
	_rx_read bitmaple1
	...

After these two changes, the text from the device information call worked flawlessly. But my B button still didn't work. I decided the B button wasn't important at this stage and went on to decoding the controller to work out how the levers functioned.

Densha-De-Go controller workings

So, after having used the standard Dreamcast controller to play hours-upon-hours of Shenmue, Shenmue II and Chu chu rocket, I would have expected at least one of the levers on the Densha-De-Go controller to use the analog joystick and the other to also use some form of analog control. After a few minutes decoding the controls, it became apparent that they simply used the buttons available (up, down, left, right, x, y, z) in a binary-value style configuration.

Throttle Position X Y Z
T0 [x] [x]
T1 [x] [x]
T2 [x]
T3 [x] [x]
T4 [x]
T5 [x]
Other Buttons Mapping
SELECT D
START START
A A
B ??
C C

Brake Position UP DOWN LEFT RIGHT
B0 [x] [x] [x]
B1 [x] [x] [x]
B2 [x] [x]
B3 [x] [x] [x]
B4 [x] [x]
B5 [x] [x]
B6 [x]
B7 [x] [x] [x]
B8 [x] [x]
EM0 [x] [x]
EM1 [x]
EM2 [x] [x]
EM3 [x]
EM4 [x]
EM5 - - - -

In between the B's there is a slight overlap of the buttons, where the current and next 'combination' is combined, but this doesn't seem to last more than one cycle.
In between the EM's the controller reads UP-DOWN-LEFT-RIGHT. This area exists between all EM's and B8-EM0 and seems to be about 3mm wide.

Controlling trains

Now it was time to get this incorporated into my original train throttle. I didn't want to have to use the Python code, so instead I 'crafted' the packets into the Arduino code.

	//here is a quick packet to request device information from controller 'one'.
	//i.e. the only controller connected.
	packet.header[0] = 0; // Number of additional words in frame
	packet.header[1] = 0; // Sender address = Dreamcast
	packet.header[2] = (1 << 5); //(1 << 5); // Recipient address = main peripheral on port 0
	packet.header[3] = 1; // Command = request device information
	packet.data[0]   = 0x21; //checksum
	packet.data_len  = 5;
	//send the above packet to initiate comms with the controller.
	maple_transact();

I'm going to gloss over this part pretty quickly, as this post was more about getting the Dreamcast controller usable rather than another lesson in physics and model railway throttles. For the code, I ended up just deciphering the packet that comes back from the controller, working out what the throttle position is and then adjusting the target speed. The main program loop then makes then increment/decrements the current speed to eventually match the target speed. This provides a very simple form of acceleration.

void control_throttle(void) {
	//quick hack, should actually do a binary 'and'
	//packet data[6] contains the throttle position, so use this to gauge
	//voltage output to pin 7 (or speed)
	switch (packet.data[6]) {
		case 0xF9: target_speed = 0;	t_pos = 0; break;
		case 0xFA: target_speed = 88;	t_pos = 1; break;
		case 0xFB: target_speed = 96;	t_pos = 2; break;
		case 0xFC: target_speed = 140;	t_pos = 3; break;
		case 0xFD: target_speed = 190;	t_pos = 4; break;
		case 0xFE: target_speed = 250;	t_pos = 5; break;
	}
}

The changing of pin 13 below lights the on-board Arduino LED to show once the loop has made the speed match the target speed.

	digitalWrite(13, LOW);
	if (target_speed > speed) {
		//accel
		speed += t_pos / 2;
	} else if (target_speed < speed) {
		//speed += abs(target_speed - speed);
		//decel
		speed--;
	} else {
		digitalWrite(13, HIGH);
	}
	//send the speed to the pin/railway
	analogWrite(7, speed);

And then... it just worked :)

DSC03744

Download the source code here.
Note that this does not include the Python code; nor will my code correctly interact with the python code anymore. The goal was always to have the Arduino talk to the Dreamcast controller directly.

As you can see, I've only implemented the throttle on the controller... I need to now bring in the brake lever as well, but this is where the physics lesson comes in. I'll be posting again soon enough with source code for acceleration and braking using this controller.

I've got a few ideas as to how to semi-realistically control the train with both levers, but I will need to do a little more reading before I get something going. I do know that in the actual Densha-De-Go game, the game complains when you have both levers on at the same time... and it seems wrong to be doing so... but then again, it'd be like doing a hill-start :) Maybe for freight trains?

If only our model railway trains had gears and could free roll!

15Oct/102

Sending full bytes over .NET Serial Ports to the Arduino

Ok, I have just spent a good two nights of my life diagnosing why I could sent all bytes up to value '127' and no further. In hindsight, it all makes perfect sense and the documentation is clear, but when you've been taught to think in strings, you might hit this very quickly.

The Scenario

I have my MAX7219 + LedControl Library set up on my Arduino and all works fine. I use two functions to control it: setLed and setRow. setLed simply takes a boolean to determine if the LED you are pointing at is to be on or off, but setRow requires a byte. This is all fairly straight-forward as each 'row' in the LED matrix has 8 LEDs, and a byte has 8 bits. So, starting from the lowest significant bit, a value of b00000001 will turn on the first LED in a specified row. (i.e. setRow(DEVICE,ROW,BITS);).

All communications between my application and the Arduino had been based on strings and so I had previously been using one character (one byte) to set one LED. Due to this being a complete waste of bandwidth, I decided that each byte I sent through the channel should be a byte to control full row of LEDs. This meant that I could therefore no longer 'see' the output as a string (or ASCII), as the characters I would create from setting the bits may no longer be in ASCII range... this was no big deal, as I could just view the byte values and decode it all myself.

So, on the client end (C#.NET Application) I started encoding the bytes from bit values. This all worked until I tried to set the last bit...

byte b = 1 | (1 << 7); //let's set the first and last LED.
string buffer = (char)b + "\0";
serialPort.WriteLine(buffer);
Data Sent LEDs lit Correct?
b00000001 1st OK
b01010101 1st, 3rd, 5th, 7th OK
b10101010 1st, 2nd, 3rd, 4th, 5th, 6th WRONG
b10000001 1st, 2nd, 3rd, 4th, 5th, 6th WRONG
b01000000 7th OK
b10000000 1st, 2nd, 3rd, 4th, 5th, 6th WRONG

What the hell was going on?

The Answer

So, after reading numerous blogs and not finding my answer, I went to the Arduino Forums and posted a topic asking for help. I was given advice to write a very simple test app to work out where the bytes were failing... but I never did get to write that app, instead I went to the MSDN site as soon as I saw that the Write() procedure could be overloaded.

And look what I found at the article on MSDN:

By default, SerialPort uses ASCIIEncoding to encode the characters. ASCIIEncoding encodes all characters greater then 127 as (char)63 or '?'. To support additional characters in that range, set Encoding to UTF8Encoding, UTF32Encoding, or UnicodeEncoding.

And guess what... ASCII Character ? is 63 in decimal and therefore b00111111 in binary!
So, whenever I was setting the 8th bit, the .NET Framework (in all it's wisdom) would translate this to a question mark as it was not expecting to send an invalid ASCII character. Ladies and Gentlemen, ASCII is only 7 bits!

The work-around?

byte[] b = new [] { 1, 127, 128, 129, 255 }; //let's set the first bit, last bit, etc...
serialPort.WriteLine(b, 0, b.Length);

And then everything just worked.

17Aug/102

Adding LEDs to a Japanese Shop

Pictures speak louder than words, so below is a quick tutorial on how to get an off-the-shelf TomyTec Japanese Shop Building lit with LED lighting. In total, this building received 6 LEDs; lantern, side-door, top floor (x2), bottom floor (x2).

Interior Lighting + Side Door

Model Railway Model Railway Model Railway

The trickiest part of this installation was the lantern that hangs out the front. I actually sliced it in half and bored out the middle to fit an LED inside. I also trimmed down the LED with a file to get it to fit a little more easily. This was done with my pocket-knife and I stopped when I felt it grinding metal. :)

Note that I borrowed ideas from this blog and I strongly recommend you check out the work the author has done on their layout!

Front Lantern

Model Railway Model Railway Model Railway
Model Railway Model Railway

And finally, everything is wired up. You can see the huge hole I accidently drilled in the side of the shop... luckily the lantern covers it over pretty well.

Finished Product

Model Railway Model Railway Model Railway