Saturday, 18 October 2014

Simplified serial string stuffing with PIC and Oshonsoft

If there's one thing we love more than alliteration at Nerd Towers, it's simplifying repetitive tasks. One thing that the Arduino platform, and it's library-based approach to coding has in it's favour, is that it makes prototyping common/simple projects quite easy.

Take serial communications, for example.
Serial.begin(9600) opens the hardware serial port at 9600 baud.
With PIC and Oshonsoft we have HSeropen 9600.
Different syntax, but the same result.

In PIC-land, it's the same as setting the appropriate BAUDCON and SPBRG registers, to get the right baud rate for the current crystal/clock speed. The Arduino IDE is probably doing something similar.

Getting data is pretty easy on both platforms, but where Arduino really shines, is it's string-handing ability. And in particular, the relative ease with which you can build up a string, from incoming serial data.

String s="";
While Serial.bytesAvailable(){
  char b=Serial.readChar();
  s+=b;
}

With PIC and Oshonsoft, we've always just dumped the data into a large buffer, and processed the incoming data. The downside to this approach is that you effectively need to stop all serial communications which you work on the received data. And this isn't something you want to do each time. There's no equivalent of Serial.bytesAvailable. And that's a shame, because, for prototyping stuff quickly, that's a really useful function.

And after messing about with those wifi modules the other night, what we really need is a quick and easy string handling routine (that can handle data coming in at no less than 115,200 bps). So we came up with an idea which, after looking at the source code for the hardware serial library in Arduino, is very similar to the Arduino implementation.

Basically, we're going to keep a "circular buffer".
This means we've got an array of 80 bytes as our buffer. We keep track of which element of the buffer we're going to write any incoming data to (called the "head" of the buffer) and which element we're going to read data from (called the "tail").

The idea is we can use an RX-data-in interrupt to write incoming data, one byte at a time, into the circular buffer, without affecting where we're reading the data back from. Whenever the RX interrupt fires, we advance the head pointer one element along the array, and store the incoming byte into that location



It's called a circular buffer because when either of the pointers need to advance beyond element 79 (the last element in an 80 byte buffer) they reset back to zero and continue filling the array.

When we want to see what data we've received over serial, we read the data out of the buffer, starting from the tail. While we're reading this data back, it's quite possible for more data to be coming in over serial, which in turn gets written to the buffer, advancing the head pointer.


Because of this, it's quite possible that the head pointer can be ahead of the tail (as is the case for the first few bytes received). But after time, it's also possible that the tail has "moved along" a few bytes, and the head pointer has "wrapped around" back to zero, and is "behind" the tail pointer in the array. This is normal.



The only time we have a problem is if the head pointer tries to "cross over" the tail pointer.
When this occurs, we've had a buffer overrun. i.e. we've received more data into our buffer than we've managed to read back out in the time it's taken to fill up (i.e. we've received 80 bytes more than we've read out of the buffer). There are two ways this could be handled - simply stop advancing the head pointer, and any further data received is lost; or advance the tail pointer just ahead of the head pointer, retaining the newest data, and overwriting the older data.

Whichever approach is used will result in lost data, and it depends on the application as to which data has a higher priority and should be kept. For simplicity, we're using the first approach - if we've received a full 80 characters into our buffer and not managed to read them out quickly enough, we keep reading the serial data from the UART module (thus avoiding a true, hardware buffer overrun which would disable the serial peripheral on the chip) but we don't store it anywhere. It's just as easy to amend the code to retain the newer data by overwriting the old data and advancing the tail pointer.

The advantage to this approach, rather than dumping incoming bytes into a "regular"  linear array, is that it allows us to monitor the incoming data for a particular character or combination of characters. If we're looking for the word "HELLO" in our data, we'd have to traverse the array looking for

array(i+0)="H" AND array(i+1)="E" AND array(i+2)="L" AND array(i+3)="L" AND array(i+4)="O"

which can get quite cumbersome. Far easier to read back the contents of the array, until a specific character (such as a line break or carriage return) is hit, and process the contents as a string of data. While processing each individual line of (ascii) data received, the interrupt-driven RX routine is  still populating the rest of the buffer - something we couldn't do if we had to process our array of data immediately after receiving the character or byte acting as a delimiter in our data.

Taking all these ideas and putting them together, we've written a simple serial buffer routine that stores data coming in and sets a flag when a particular character (in the example, it's a carriage return) is received into the UART receiver.

In our example, we demonstrate how processing the data can continue outside of receiving characters on the serial UART peripheral; our example code can detect if a line in the serial data contains the word "OK" (and flashes an LED if it does) or sends the data back out to the serial TX (for debugging) after a delay of about a second. Of course, this doesn't do much on it's own, but it's a great start, for parsing incoming ASCII messages from the wifi module; especially because responses are often written as AT type commands, one on each line, with either OK or ERROR written on it's own line to indicate the success (or otherwise) of each command sent to the wifi module.

It's not quite as elegant as the Arduino's while Serial.bytesAvailable() function - but it's far easier than either handling characters as soon as they arrive at the serial RX port, or traversing a byte array of characters, to see which words they make up!



Define CONFIG1 = 0x0984
Define CONFIG2 = 0x1cff
Define CLOCK_FREQUENCY = 32
Define STRING_MAX_LENGTH = 79
AllDigital

symbols:
     Symbol led_pin = PORTC.2
     Symbol tx = PORTA.0
     Symbol rx = PORTA.1
   
declarations:
     Dim serial_ready As Bit
     Dim serial_buffer(80) As Byte
     Dim serial_head As Byte
     Dim serial_tail As Byte
     Dim serial_rx As Byte
     Dim serial_string As String
     Dim serial_delimiter As Byte
     Dim serial_ignore As Byte
     Dim i As Byte
   
configpins:
     ConfigPin led_pin = Output
     ConfigPin rx = Input
     ConfigPin tx = Output
   
initialise:
     'set the oscillator to 32mhz internal
     OSCCON = 11110000b 'oscillator = 32mhz
     WaitMs 50 'wait for internal voltages to settle

     'put the tx/rx lines onto the programming pins for easy development!
     APFCON0.TXCKSEL = 1 'tx onto RA.0
     APFCON0.RXDTSEL = 1 'rx onto RA.1
   
     'create an interrupt on the serial receive.
     'as data comes in, add to the circular buffer
     PIE1.RCIE = 1
     INTCON.PEIE = 1
     INTCON.GIE = 1
   
     'blank the buffers
     For i = 0 To 79
           serial_buffer(i) = 0
     Next i
     serial_string = ""
     serial_head = 0
     serial_tail = 0
   
     'open the hardware serial port
     Gosub serial_open
   
     'testing:
     'flash the led to show we're alive
     For i = 1 To 4
           High led_pin
           WaitMs 500
           Low led_pin
           WaitMs 500
     Next i
   
     serial_string = "Ready" + CrLf
     Gosub serial_send
   
     'if we're receiving ascii data over serial, each line ends with CrLf
     serial_delimiter = 0x0d
     serial_ignore = 0x0a
   
     'prepare the serial buffer
     serial_ready = 0
   
loop:
     If serial_ready = 1 Then
   
           serial_ready = 0
   
           'testing:
           led_pin = Not led_pin
         
           Gosub get_serial_string
         
           If serial_string = "OK" Then
                 For i = 0 To 4
                       High led_pin
                       WaitMs 100
                       Low led_pin
                       WaitMs 100
                 Next i
           Else
                 'testing:
                 WaitMs 1000
                 Gosub serial_send
           Endif
     Endif
Goto loop
End

On Interrupt
     Save System
   
     If PIR1.RCIF = 1 Then
   
           'get the byte from the rx buffer to clear the RCIF flag
           serial_rx = RCREG
           If serial_rx = serial_delimiter Then
                 'this is a carriage return (or a zero-byte) character
                 serial_ready = 1
           Else
                 If serial_rx = serial_ignore Then
                       'this is a linefeed character so ignore
                 Else
                       'store the received byte in the serial buffer
                       serial_head = serial_head + 1
                       If serial_head > 79 Then serial_head = 0
                       If serial_head = serial_tail Then
                             'crash!
                             'don't put any more data into the buffer until you've read some back
                             '(or maybe empty the older data first?)
                       Else
                             serial_buffer(serial_head) = serial_rx
                       Endif
                 Endif
           Endif
     Endif
Resume

get_serial_string:
     Dim buffer_start As Byte
     Dim buffer_end As Byte
     Dim buffer_end2 As Byte
     Dim k As Byte
   
     serial_string = ""
     buffer_start = serial_tail + 1
     If buffer_start > 79 Then buffer_start = 0
   
     If serial_head > serial_tail Then
           'this is easy
           buffer_end = serial_head
           buffer_end2 = 0xff
     Else
           'the head has reached 80 and wrapped around to zero
           buffer_end = 79
           buffer_end2 = serial_head
     Endif

     For i = buffer_start To buffer_end
           k = serial_buffer(i)
           serial_string = serial_string + Chr(k)
     Next i
     If buffer_end2 <> 0xff Then
           For i = 0 To buffer_end2
                 k = serial_buffer(i)
                 serial_string = serial_string + Chr(k)
           Next i
     Endif
   
     'remember where to start reading from next time
     serial_tail = serial_head

Return

serial_open:
     BAUDCON.4 = 0 'SCKP synchronous bit polarity
     BAUDCON.3 = 0 'BRG16 enable 16 bit brg
     BAUDCON.1 = 0 'WUE wake up enable off
     BAUDCON.0 = 0 'ABDEN auto baud detect

     TXSTA.6 = 0 'TX9 8 bit transmission
     TXSTA.5 = 1 'TXEN transmit enable
     TXSTA.4 = 0 'SYNC async mode
     TXSTA.3 = 0 'sednb break character
     TXSTA.2 = 0 'BRGH high baudrate
     TXSTA.0 = 0 'TX9D bit 9

     RCSTA.7 = 1 'SPEN serial port enable
     RCSTA.6 = 0 'RX9 8 bit operation
     RCSTA.5 = 1 'SREN enable receiver
     RCSTA.4 = 1 'CREN continuous receive enable

     SPBRGH = 0 'brg high byte
     SPBRG = 51 'brg low byte(see datasheet For 9600@32mhz)

Return

serial_send:
     Dim k As Byte
     Dim j As Byte
   
     k = Len(serial_string)
     k = k - 1
     For i = 0 To k
           j = serial_string(i)
           TXREG = j
           While TXSTA.TRMT = 0
                 'wait
           Wend
     Next i
   
     'send a carriage return/line feed combo at the end of the line
     TXREG = 0x0d
     While TXSTA.TRMT = 0
           'nothing
     Wend
     TXREG = 0x0a
     While TXSTA.TRMT = 0
           'nothing
     Wend
   
     serial_string = ""
   
Return

Friday, 17 October 2014

Getting an ESP8266 wifi module to work with Arduino


Last night was another BuildBrighton nerd-meet-up and, luckily, we had a couple of these new fangled ESP8266 wifi modules to try out. In case you've been living in a cave with a tin can tied to the end of piece of string as an internet connection, you'll probably know that these are the ultra-cheap wifi modules currently flooding out of Chinese factories and onto "hacker benches" all over the world.

The reason they've created such a stir is that a) they're cheap and b) the firmware can be (relatively) easily upgraded. So hackers and makers all over the world have been busy trying to access the onboad microcontroller and utilise the extra GPIO pins, to create a single-chip, all-in-one wifi controller.

Our interests are less adventurous - the modules are sold as UART-to-wifi plugin devices, and that's exactly how we're going to use them.




Supposedly you need just four wires to get your microcontroller-based project talking to a home network over wifi. In theory, it's dead simple to bung this into a project and talk to the world. practice, we found a few gotchas which took up most of the evening to resolve - but we got there in the end.

The first thing to do is to power up the device and get some AT commands flowing.
This is where the first gotcha is waiting, for the unwary. It's documented all over the 'net that these little things can get quite power hungry - they can demand upwards of 280mA when sending/receiving data. After 30 minutes or so of operation, they also run slightly warm to the touch. Nothing that could cause skin burns, but noticeably warm.

There's always the old debate about powering devices from a USB port. Some people claim to run 500mA devices straight off a USB port. Some of us have experience of Windows ejecting USB devices that demand more than their allocated 100mA, without specifically requesting it. Who is right depends pretty much on how your USB ports are configured. But if you're going to try powering these off a USB-to-serial converter, they can still work. But they also reset spasmodically too and without warning.

  • So gotcha number 1; use a dedicated power supply (3.3.v)

We did this, connected up the uart lines and ran a terminal emulator at 57,600 bps as per the datasheet. There was nothing on the screen. The red light was on, on the module, but no data coming from ip. Touching the pins on the top of the board caused the onboard blue led to flicker, and a few characters of junk appeared on the screen - nothing useable, but something!

Some sources on the internet say you can leave the unused four pins floating. Some people say they need to be pulled high (though anything between 1k and 10k resistors). Some sites say you can simply connect these to Vcc. We tried all of these and still got no response.

To actually get the device to respond, we found we had to pull the RESET and CH_PD lines high  but leave the GPIO lines low/floating. Pulling all four pins high stopped the module from sending data.

  • Gotcha number 2: pull the RESET + CH_PID lines high, but not the GPIO lines.

Toggling the reset line at this point and suddenly the screen was full of gunk! A lot of sources say this is normal, and after a whole load of junk, you can expect to see the word "ready". In practice, we found that the data was garbled with no "ready" signal. Changing the baud rate to 115,200 bps fixed this

Gotcha number 3: Some modules use 56,700 baud, some use 115,200bps. Try both

Eventually we started seeing something meaningful in the terminal window



Now we did spend quite some time banging AT command in and getting any number of peculiar responses, ranging from an empty string, to "Error", the bizarre "link is not", to the rather more cryptic "no this fun".

Once you have a module responding to AT commands, the fun begins. It took a lot of Googling around, and reading lots of blog posts from people who tried, gave up, tried, fried-the-board-and-bought-another-to-find-it-worked-differently and a fair bit of guesswork to get our modules to work the way we wanted to, but eventually we did manage to establish a connection between our wifi module and a PC, over a home network. This may not be your preferred setup, but this is what we were aiming for, and how we got there:

We wanted to connect the wifi module onto our home network, and have it report back the IP address it had been assigned. We would then create an app (to run on a smartphone or tablet) into which we could enter this IP address, and send and receive commands over the home network, between the wifi module and the tablet.

This means setting up the wifi module as a "station" (rather than an "access point") but also to set it up as a "server" - i.e. to accept incoming connections. As you can imagine, there can be any amount of confusion when some people refer to a "station" as a "client", but then set up the network connection mode as "server". Server!=client. So we're sticking with the terminology from the Chinglish datasheet.

The first thing to do is set the device as a station:

AT+CWMODE = 1

1=station, 2=AP, 3=station + AP (some kind of weird hybrid mode).
Now, query the local access points with:

AT+CWLAP

And the response (sometimes) looks something like this:


Occasionally this fails. Sometimes the module locks up entirely. There are a number of guesses at why this might be - some people favour that hidden APs screw things up, some that too many basically fill the buffer(s) and cause it to lock up, some just that the firmware is crapping out for some unrelated reason. We've had mixed success using the CWLAP command - sometimes we do get a list of access points. And sometimes it locks up so that it needs to be power-cycled to become responsive again.

Once you have the SSID of the access point you're looking for, connect using

AT+JAP="ssid_goes_here","password_here"

And wait for the OK response.
Interestingly, you get OK even if you put an invalid SSID and/or password. OK doesn't mean "connected to the access point ok". It just means "OK, I heard you". The way to test if you're actually connected to the access point is to give it a few seconds, for the negotiations to complete, then query the IP address, using:

AT+CIFSR

The response will either be ERROR (no ip address assigned) or a string with the ip address in it. Confusingly, sometimes the module requires =? to query a property, sometimes just ? (no equals sign) and sometimes neither/none. So far, we've avoiding using the ? to query anything, but that's coming....

If you've got an ip address, your device is connected on the network and you should be able to see it in the list of connected devices on your router/access point admin page.

Interestingly, the wifi module keeps it's connection data in local eeprom, so that it can silently reconnect to a network, if the connection drops. This is very useful (since we don't have to worry about storing SSID and password data in our microcontroller eeprom and attempting to reconnect on power-up: just give the wifi module power and it'll try to connect to the previous access point, if it's still available) but can also be a problem if the device is to be used for anything important.

The wifi module can be subject to the Google Chromecast attack and we're still not sure whether this is desirable behaviour. Here's how the Chromecast attack works:

When the wifi module powers on, it looks for the previous access point (SSID) and connects using the stored password, if  possible. If the original access point is not available, but a spoof or clone access point exists, with the same SSID and accepts incoming connections (is open, or uses the same password) the wifi module will happily connect to the spoof  access point, without reporting any error,



We proved that this was possible with the wifi modules too, by each setting up a wifi hotspot on a Samsung Galaxy S3, using the SSID "test". Each of us set up our phone as a wifi hotspot/access point with the SSID "test", and activated them in turn. In between activating the access points, we power-cycled the wifi module and it firstly connected to Chris' phone, then Steve's, then Jake's phone - each time completely silently, and without reporting that the access point had changed: it basically found an access point with the name "test" and connected using the same credentials as last time.

This does add a resilience into the wifi module - if the connection to the AP drops, the wifi module will silently reconnect, and resume the same ip address as before, and you'd never be any the wiser. But it does mean that your wifi-based project is subject to spoof attacks (should someone be able to reset your home router, and set up their own device as an access point with the same name!)

By this point, we'd managed to get our wifi module onto a home network (it was actually one of the many APs at the hackspace, but the principle is the same!) and to see it appear in the list of connected devices. Now we wanted to get some data exchange going:

AT+CIPSERVER supposedly sets up the device as a server, ready to accept incoming connections, but every time we tried this command, we just got "Error" as a response. A lot of Googling and we still had no answer - except some people simply said that this command doesn't work, and others saying that the firmware needed to be updated.

We found the solution to be a little simpler.
Before the module can accept (multiple) incoming connections, it needs to be put into "accept multiple connections" mode, using the command:

AT+CIPMUX=1

Now, the command AT+CIPSERVER=1,4040 gave the exciting response OK
It looked very much - just as we approached the midnight-deadline-for-getting-things-done that we had a wifi connected server, awaiting connections on port 4040.

After a few false starts with putty (starting with trying to connect over serial in SSH mode, instead of "raw" mode) Other Chris suggested just entering the ip address of the wifi module into a browser address bar. Not expecting anything to happen, and given the late hour, just about ready to give up, we tried it, in desperation:

192.168.43.121: 4040

And, amazingly, the terminal window showed the response:



We had made first contact!
The + IPD message shows us that the response contained 355 bytes. More importantly, it first shows us the "channel number" (or client ID) of the incoming connection. This is important, and this is how we can send the correct data to the appropriate connected client. Following the id number and the length of the message is the main body of the message received.

To send a response, we used the AT command:

AT+CIPSEND=0, 5

which says "send 5 bytes to channel zero".
After committing the command (i.e. hitting the cr+lf combination) the prompt changed to a > symbol to indicate that we were now entering data, not commands.



After entering "Hello" and hitting enter....... nothing happened.
The "loading" icon was still spinning on the web page we'd opened, to connect to the wifi module. It took about 90 seconds to time out. But when it did, the web page had changed:


It looked like we actually had some kind of two-way communication!
So we fired up Putty, only this time, taking care to use the "raw" rather than SSH mode...



... and started typing. This time, we had data!



So we sent some data back



And Putty duly showed the data immediately.



Data appeared on each end immediately. There was no noticeable lag - as soon as we hit enter on one terminal window, the data appeared in the other. By which time, after high fives all round, it was time to go home.

More investigations will continue over the weekend...









Wednesday, 15 October 2014

Nokia 5110 screen module with PIC microcontroller and Oshonsoft

A little while back, we played with a neat little - cheap - LCD screen from an old-fashioned Nokia phone; now resold as "hacker's toys" online as SPI modules, these screens are great for displaying something a bit more interesting than just 16x2 lines of character-based text.

How these displays work is explained in a little more detail in our earlier post http://nerdclub-uk.blogspot.co.uk/2014/08/sending-bender-over-air-to-nokia-5110.html.

But we'd been using some of Steve's lovely Arduino libraries for a lot of it, which meant a lot of the low-level stuff was missing. There are loads of libraries available online to draw text and numbers onto these displays, but they almost always use fixed-width fonts.

Having left the 1980's behind, we decided to write our own "libraries" to write text onto the Nokia 5100 display, using a PIC microcontroller. The code below was written for a 16F877a (because it was the first chip we found already on a breadboard, with a programming header connected) but would work equally well on any PIC microcontroller with enough RAM for the font table.

Define CONFIG = 0x3f32
Define CLOCK_FREQUENCY = 20

AllDigital
Symbol led_pin = PORTB.7
Symbol lcd_sck = PORTD.4
Symbol lcd_data = PORTD.5
Symbol lcd_command = PORTD.6
Symbol lcd_reset = PORTD.7

definitions:
     
     Define STRING_MAX_LENGTH = 30
     
     Dim i As Byte
     Dim j As Byte
     Dim k As Byte
     
     Dim li As Byte
     Dim lj As Byte
     Dim lk As Byte
     
     Dim si As Byte
     Dim sk As Byte
     
     Dim byte_to_send As Byte
     Dim x_value As Byte
     Dim y_value As Byte
     Dim character_to_send As Byte
     Dim char(8) As Byte
     Dim move_x As Byte
     Dim string_to_write As String
     
     Dim invert_display As Bit
     Dim wrap_text As Bit
           
init:
     ConfigPin PORTB = Output
     ConfigPin PORTD = Output
     
     invert_display = 0
     wrap_text = 0
     
     WaitMs 100
     
     Gosub init_lcd
     Gosub lcd_clear
     
     x_value = 2 'columns are individual pixels
     y_value = 0 'must be a row number, which is a multiple of 8
     Gosub lcd_goto_xy
     Gosub draw_band
     
     
     x_value = 2
     y_value = 1
     Gosub lcd_goto_xy
     invert_display = 1
     wrap_text = 0
     string_to_write = "{ ABCDEFGHIJKLMNOPQRSTUVWXYZ"
     Gosub lcd_write_string
     invert_display = 0
     
     x_value = 2
     y_value = 3
     wrap_text = 1
     Gosub lcd_goto_xy
     string_to_write = "abcdefghijklmnopqrstuvwxyz"
     Gosub lcd_write_string
     
     x_value = 2
     y_value = 5
     Gosub lcd_goto_xy
     string_to_write = "123.45.67.89:0"
     Gosub lcd_write_string

loop:
     
     High led_pin
     WaitMs 2000
     Low led_pin
     WaitMs 2000
                       
Goto loop
End

init_lcd:
     Low lcd_reset
     WaitMs 10
     High lcd_reset
     
     'put into command mode
     Low lcd_command
     
     byte_to_send = 0x21 'lcd extended commands
     Gosub lcd_write_byte
     byte_to_send = 0xb1 'set lcd contrast
     Gosub lcd_write_byte
     byte_to_send = 0x04 'set temp coefficient
     Gosub lcd_write_byte
     byte_to_send = 0x14 'lcd bias mode 1:48
     Gosub lcd_write_byte
     byte_to_send = 0x0c 'put lcd into normal mode
     Gosub lcd_write_byte
     byte_to_send = 0x20 'dunno
     Gosub lcd_write_byte
     byte_to_send = 0x0c 'dunno
     Gosub lcd_write_byte
     WaitMs 5
     
     'put into data mode
     High lcd_command

Return

lcd_write_byte:
     For li = 0 To 7
           lk = 7 - li
           lj = 1
           lj = ShiftLeft(lj, lk)
           lj = lj And byte_to_send
           If lj = 0 Then
                 'send a zero bit
                 Low lcd_data
           Else
                 'send a one bit
                 High lcd_data
           Endif
           
           'toggle the clock line
           High lcd_sck
           ASM: nop
           Low lcd_sck
           
     Next li
Return

lcd_clear:

     'put the cursor at 0,0
     x_value = 0
     y_value = 0
     Gosub lcd_goto_xy
     
     'put the lcd into data mode
     High lcd_command
     
     'send 504 (48*84/8) blank pixels
     For i = 0 To 5
           For j = 0 To 83
                 byte_to_send = 0x00
                 If invert_display = 1 Then byte_to_send = byte_to_send Nand 0xff
                 Gosub lcd_write_byte
           Next j
     Next i
     
     'just give everything a sec or two
     WaitMs 10
     
Return

lcd_goto_xy:
     Low lcd_command
     byte_to_send = 0x80
     byte_to_send = byte_to_send Or x_value
     Gosub lcd_write_byte
     
     byte_to_send = 0x40
     byte_to_send = byte_to_send Or y_value
     Gosub lcd_write_byte
     High lcd_command
Return

lcd_get_character:

     'set the default character to zero
     char(0) = 0x00
     char(1) = 0x00
     char(2) = 0x00
     char(3) = 0x00
     char(4) = 0x00
     char(5) = 0x00
     char(6) = 0x00
     move_x = 0

     Select Case character_to_send
           Case "A"
           char(0) = 01111110b
           char(1) = 00001001b
           char(2) = 00001001b
           char(3) = 01111110b
           move_x = 5
           
           Case "B"
           char(0) = 01111111b
           char(1) = 01001001b
           char(2) = 01001001b
           char(3) = 00110110b
           move_x = 5

           Case "C"
           char(0) = 00011100b
           char(1) = 00100010b
           char(2) = 01000001b
           char(3) = 01000001b
           move_x = 5

           Case "D"
           char(0) = 01111111b
           char(1) = 01000001b
           char(2) = 01000001b
           char(3) = 00100010b
           char(4) = 00011100b
           move_x = 6
                       
           Case "E"
           char(0) = 01111111b
           char(1) = 01001001b
           char(2) = 01000001b
           move_x = 4
           
           Case "F"
           char(0) = 01111111b
           char(1) = 00001001b
           char(2) = 00001001b
           move_x = 4

           Case "G"
           char(0) = 00011100b
           char(1) = 00100010b
           char(2) = 01000001b
           char(3) = 01001001b
           char(4) = 01111010b
           move_x = 6

           Case "H"
           char(0) = 01111111b
           char(1) = 00001000b
           char(2) = 00001000b
           char(3) = 01111111b
           move_x = 5
           
           Case "I"
           char(0) = 01000001b
           char(1) = 01111111b
           char(2) = 01000001b
           move_x = 4

           Case "J"
           char(0) = 00100000b
           char(1) = 01000000b
           char(2) = 01000001b
           char(3) = 00100001b
           char(4) = 00011111b
           move_x = 6

           Case "K"
           char(0) = 01111111b
           char(1) = 00001000b
           char(2) = 00010100b
           char(3) = 01100011b
           move_x = 5

           Case "L"
           char(0) = 01111111b
           char(1) = 01000000b
           char(2) = 01000000b
           move_x = 4
           
           Case "M"
           char(0) = 01111111b
           char(1) = 00000001b
           char(2) = 00000010b
           char(3) = 00001100b
           char(4) = 00000010b
           char(5) = 00000001b
           char(6) = 01111111b
           move_x = 8

           Case "N"
           char(0) = 01111111b
           char(1) = 00000100b
           char(2) = 00001000b
           char(3) = 00010000b
           char(4) = 01111111b
           move_x = 6

           Case "O"
           char(0) = 00011100b
           char(1) = 00100010b
           char(2) = 01000001b
           char(3) = 01000001b
           char(4) = 00100010b
           char(5) = 00011100b
           move_x = 7

           Case "P"
           char(0) = 01111111b
           char(1) = 00010001b
           char(2) = 00010001b
           char(3) = 00001110b
           move_x = 5
           
           Case "Q"
           char(0) = 00011100b
           char(1) = 00100010b
           char(2) = 01000001b
           char(3) = 01010001b
           char(4) = 00100010b
           char(5) = 01011100b
           move_x = 7
           
           Case "R"
           char(0) = 01111111b
           char(1) = 00011001b
           char(2) = 00101001b
           char(3) = 01000110b
           move_x = 5

           Case "S"
           char(0) = 00100010b
           char(1) = 01000101b
           char(2) = 01001001b
           char(3) = 00110010b
           move_x = 5
           
           Case "T"
           char(0) = 00000001b
           char(1) = 00000001b
           char(2) = 01111111b
           char(3) = 00000001b
           char(4) = 00000001b
           move_x = 6
           
           Case "U"
           char(0) = 00111111b
           char(1) = 01000000b
           char(2) = 01000000b
           char(3) = 01111111b
           move_x = 5
           
           Case "V"
           char(0) = 00000011b
           char(1) = 00011100b
           char(2) = 01100000b
           char(3) = 00011100b
           char(4) = 00000011b
           move_x = 6
           
           Case "W"
           char(0) = 00111111b
           char(1) = 01000000b
           char(2) = 00100000b
           char(3) = 00011000b
           char(4) = 00100000b
           char(5) = 01000000b
           char(6) = 00111111b
           move_x = 8

           Case "X"
           char(0) = 01100011b
           char(1) = 00010100b
           char(2) = 00001000b
           char(3) = 00010100b
           char(4) = 01100011b
           move_x = 6
           
           Case "Y"
           char(0) = 00000011b
           char(1) = 00000100b
           char(2) = 01111100b
           char(3) = 00000111b
           move_x = 5
           
           Case "Z"
           char(0) = 01111001b
           char(1) = 01001001b
           char(2) = 01001101b
           char(3) = 01001011b
           move_x = 5
                       
           Case "a"
           char(0) = 00100000b
           char(1) = 01010100b
           char(2) = 01010100b
           char(3) = 01111000b
           move_x = 5

           Case "b"
           char(0) = 01111111b
           char(1) = 01000100b
           char(2) = 01000100b
           char(3) = 00111000b
           move_x = 5
           
           Case "c"
           char(0) = 00111000b
           char(1) = 01000100b
           char(2) = 01000100b
           char(3) = 01000100b
           move_x = 5
           
           Case "d"
           char(0) = 00111000b
           char(1) = 01000100b
           char(2) = 01000100b
           char(3) = 01111111b
           move_x = 5
           
           Case "e"
           char(0) = 00111000b
           char(1) = 01010100b
           char(2) = 01010100b
           char(3) = 01011100b
           move_x = 5
           
           Case "f"
           char(0) = 01111110b
           char(1) = 00001001b
           char(2) = 00000001b
           move_x = 4
           
           Case "g"
           char(0) = 10011000b
           char(1) = 10100100b
           char(2) = 10100100b
           char(3) = 01111100b
           move_x = 5
           
           Case "h"
           char(0) = 01111111b
           char(1) = 00000100b
           char(2) = 00000100b
           char(3) = 01111000b
           move_x = 5
           
           Case "i"
           char(0) = 01000100b
           char(1) = 01111101b
           char(2) = 01000000b
           move_x = 4
           
           Case "j"
           char(0) = 10000100b
           char(1) = 10000101b
           char(2) = 01111100b
           move_x = 4
           
           Case "k"
           char(0) = 01111111b
           char(1) = 00010000b
           char(2) = 00010000b
           char(3) = 01101100b
           move_x = 5
           
           Case "l"
           char(0) = 01000001b
           char(1) = 01111111b
           char(2) = 01000000b
           move_x = 4
           
           Case "m"
           char(0) = 01111100b
           char(1) = 00000100b
           char(2) = 00000100b
           char(3) = 01111000b
           char(4) = 00000100b
           char(5) = 00000100b
           char(6) = 01111000b
           move_x = 8
           
           Case "n"
           char(0) = 01111100b
           char(1) = 00000100b
           char(2) = 00000100b
           char(3) = 01111000b
           move_x = 5
           
           Case "o"
           char(0) = 00111000b
           char(1) = 01000100b
           char(2) = 01000100b
           char(3) = 00111000b
           move_x = 5
           
           Case "p"
           char(0) = 11111100b
           char(1) = 00100100b
           char(2) = 00100100b
           char(3) = 00011000b
           move_x = 5
           
           Case "q"
           char(0) = 00011000b
           char(1) = 00100100b
           char(2) = 00100100b
           char(3) = 11111100b
           move_x = 5
           
           Case "r"
           char(0) = 01111000b
           char(1) = 00000100b
           char(2) = 00000100b
           char(3) = 00000100b
           move_x = 5
           
           Case "s"
           char(0) = 01001000b
           char(1) = 01010100b
           char(2) = 01010100b
           char(3) = 01010100b
           char(4) = 00100000b
           move_x = 6
           
           Case "t"
           char(0) = 00111111b
           char(1) = 01000100b
           char(2) = 01000100b
           move_x = 4
           
           Case "u"
           char(0) = 00111100b
           char(1) = 01000000b
           char(2) = 01000000b
           char(3) = 01111100b
           move_x = 5
           
           Case "v"
           char(0) = 00011100b
           char(1) = 00100000b
           char(2) = 01000000b
           char(3) = 00100000b
           char(4) = 00011100b
           move_x = 6
           
           Case "w"
           char(0) = 00111100b
           char(1) = 01000000b
           char(2) = 00100000b
           char(3) = 00010000b
           char(4) = 00100000b
           char(5) = 01000000b
           char(6) = 00111100b
           move_x = 8
           
           Case "x"
           char(0) = 01101100b
           char(1) = 00010000b
           char(2) = 00010000b
           char(3) = 01101100b
           move_x = 5
           
           Case "y"
           char(0) = 10001100b
           char(1) = 10010000b
           char(2) = 10010000b
           char(3) = 01111100b
           move_x = 5
           
           Case "z"
           char(0) = 01100100b
           char(1) = 01010100b
           char(2) = 01001100b
           move_x = 4
           
           Case "0"
           char(0) = 00111110b
           char(1) = 01000001b
           char(2) = 01000001b
           char(3) = 00111110b
           move_x = 5
           
           Case "1", 1
           char(0) = 01000010b
           char(1) = 01111111b
           char(2) = 01000000b
           move_x = 4
           
           Case "2", 2
           char(0) = 01100010b
           char(1) = 01010001b
           char(2) = 01010001b
           char(3) = 01001110b
           move_x = 5
           
           Case "3", 3
           char(0) = 01000010b
           char(1) = 01001001b
           char(2) = 01001001b
           char(3) = 00110110b
           move_x = 5
           
           Case "4", 4
           char(0) = 00011111b
           char(1) = 00010000b
           char(2) = 00010000b
           char(3) = 01111100b
           char(4) = 00010000b
           move_x = 6
           
           Case "5", 5
           char(0) = 00100111b
           char(1) = 01001001b
           char(2) = 01001001b
           char(3) = 00110001b
           move_x = 5
           
           Case "6", 6
           char(0) = 00111110b
           char(1) = 01001001b
           char(2) = 01001001b
           char(3) = 00110010b
           move_x = 5
           
           Case "7", 7
           char(0) = 00000001b
           char(1) = 01110001b
           char(2) = 00011001b
           char(3) = 00000111b
           move_x = 5
           
           Case "8", 8
           char(0) = 00110110b
           char(1) = 01001001b
           char(2) = 01001001b
           char(3) = 00110110b
           move_x = 5
           
           Case "9", 9
           char(0) = 00100110b
           char(1) = 01001001b
           char(2) = 01001001b
           char(3) = 00111110b
           move_x = 5
                             
           Case "."
           char(0) = 01100000b
           char(1) = 01100000b
           move_x = 3
           
           Case ":"
           char(0) = 01100110b
           char(1) = 01100110b
           move_x = 3

           Case 32
           move_x = 4
           
           Case "{"
           Gosub draw_wifi
           
     EndSelect
     
Return

send_character_to_lcd:
     
     'put the lcd into data mode
     High lcd_command

     For i = 0 To 6
           byte_to_send = char(i)
           If invert_display = 1 Then byte_to_send = byte_to_send Nand 0xff
           Gosub lcd_write_byte
     Next i
           
Return

lcd_write_string:

     sk = Len(string_to_write) - 1
     For si = 0 To sk
     
           'convert the requested character into an array of bits to display
           character_to_send = string_to_write(si)
           Gosub lcd_get_character
           
           'check the character will fit on the screen
           k = x_value + move_x
           
           If k > 82 And wrap_text = 0 Then
                 'don't write anything more on this line
                 x_value = 84
           Else
                 If k > 82 Then
                       x_value = 2
                       y_value = y_value + 1
                       Gosub lcd_goto_xy
                 Endif
           
                 'send the character to the lcd
                 Gosub send_character_to_lcd

                 'update the "cursor x" position
                 x_value = x_value + move_x
                 Gosub lcd_goto_xy
           Endif
                 
     Next si
Return

draw_band:
     x_value = 0
     Gosub lcd_goto_xy
     
     'put the lcd into data mode
     High lcd_command

     byte_to_send = 11110000b
     If invert_display = 1 Then byte_to_send = byte_to_send Nand 0xff
     For i = 0 To 83
           Gosub lcd_write_byte
     Next i
     
     byte_to_send = 0xff
     If invert_display = 1 Then byte_to_send = byte_to_send Nand 0xff
     For i = 0 To 83
           Gosub lcd_write_byte
     Next i
     
     byte_to_send = 00000111b
     If invert_display = 1 Then byte_to_send = byte_to_send Nand 0xff
     For i = 0 To 83
           Gosub lcd_write_byte
     Next i
     
     
Return

draw_wifi:

     For i = 0 To 14
           Select Case i
                 Case 0, 14
                 byte_to_send = 00001000b
           
                 Case 1, 13
                 byte_to_send = 00000100b
           
                 Case 2, 12
                 byte_to_send = 00000010b
           
                 Case 3, 11
                 byte_to_send = 00010010b
           
                 Case 4, 10
                 byte_to_send = 00001001b
           
                 Case 5, 9
                 byte_to_send = 00100101b
           
                 Case 6, 8
                 byte_to_send = 10010101b
           
                 Case 7
                 byte_to_send = 11010101b
           EndSelect
           
           If invert_display = 1 Then byte_to_send = byte_to_send Nand 0xff
           Gosub lcd_write_byte
     Next i
     
     x_value = x_value + 16
     Gosub lcd_goto_xy

Return

We've written our font "table" as a select case statement.
When it come to being compiled down, a look up table and a select case statement are very similar anyway (and we just prefer using IF statements - just ask Steve!).

The font values are written in long-hand binary, rather than in decimal or their short-hand hex values. This makes no difference whatsoever to the final code (since the compiler treats all constant values the same, however they are written).

We stuck with long-hand binary, as it helps us to "see" the fonts and graphics inside the source code;


Drawing to the screen is as simple as sending an x and a y co-ordinate value (x is any number 0-83 and y is any "row" from 0 to 5, representing an actual co-ordinate value of 0, 8, 16, 32, 40 or 48). The next eight bits (single byte) are then used to draw "upwards" from the cursor position. If more than 8 bits are sent, the cursor position returns to the original y axis value, and the x-position shifts across one pixel.

There are also a couple of extras built into this example.
You can set the "inverted" flag on and off, to draw white text over a black background, or black text on a white background (the background colour should be fully drawn beforehand).
There's also a simple routine to draw a horizontal bar across the screen, to allow inverted text to be seen against a plain/blank background. An example of this in use would be a menu highlight system.

We also added a "text wrap" toggle, which determines - if the text is two large to fit onto one row - whether or not to break the work/string and draw it across two or more rows.

So far we've got upper and lower case letters, numbers and a few punctuation marks.
We're also using special characters to draw bitmaps at the cursor location (this isn't the correct use of the "library" - we were just keen to see how non-alphanumeric characters appeared!)

Heres' a photo of the screen in action:


We feel that the non-fixed width font just looks that little bit more "friendly" and less "hacky" and "homemade" than other library-based fonts.

The code above could easily be ported to another platform (such as Arduino).
We deliberately avoided using the hardware SPI peripheral, to allow it to be ported to any other microcontroller, without spoiling existing libraries or limit it to only those devices with a free hardware SPI module.

We really like these displays and - for a quid more than a 16x2 line character LCD display - we're looking at using these in a future wifi-based project (just as soon as our ESP8266 modules arrive!)

Making 28mm terrain from greenstuff and home made moulds

Recently a few of the nerds were quite taken by a Kickstarter project called Basius. It's a set of resin moulds from which you can create interesting-looking "base toppers" for 28mm miniatures. A few of us even pledged for one, to see what they were like.

Having spent the last few days in front of a computer screen (whether for real work, or debugging the latest nerd project) it was nice idea to get away from the computer for a few hours and make something a bit more "arts and crafts-y". There's quite a bit going on, other than just nerding about, at Nerd Towers at the minute, so a bit of escapism is always welcome!

Using the Basius project as inspiration (and being impatient because the campaign is still running and the products aren't likely to be shipped for a few months yet) we set about creating our own simple moulds, from some "junk masters".

The first thing we did was stick some "industrial" looking bits of plastic to a base. These are easily picked up at local hobby shops, by rummaging around through junk boxes, or raiding charity shops for old, broken toys. As we're going for an industrial look, anything with plates and rivets was fair game - along with some herring-bone patterned squares (which looks like the metal diamond floor covering on gantries and the like) and various doors and hatches. We even dismantled a cheap metal sieve (not shown) to get a nice, textured effect for a floor.


Next, we used some Instant Mould. This is a low-melting point thermo-plastic and has a variety of different names, such as Polymorph, PolyForm, Shapelock, and the super-catchy sounding Polycaprolactone. Basically you bung this into a cup/pan of boiling water for a few minutes, and it goes all soft and sqidgy. Take it out, and you have maybe a minute or two, to form it over your "master" shapes, to create a negative mould.


Here we were probably a little stingy with the Polymorph - a thicker layer over a smaller area would have created a much more rigid/easy-to-use mould in the long run.

Douse the plastic in cold water to help it set more quickly.
This stuff is amazing. Hardly anything sticks to it - not even superglue!
As a result, it pops off the master really easily.


Although the top surface is uneven and it doesn't look particularly brilliant from this side, on the underside - where the business end of things is - it has captured even the tiniest of details perfectly:


So all that's left is to try out our new moulds.
Using some two-part epoxy putty (often called kneadatite, or simply green-stuff) we made up some little balls of green goop and pressed it onto a couple of 28mm bases


This stuff is really sticky when fresh, and is hard to weed out of little nooks and crannies - which also makes it great for picking up detail from small moulds. So we simply wet the top side of the green stuff (the side that was about to come into contact with the mould) and then smooshed it into the negative (is smooshed even a word?)


For a first attempt, these didn't come out too bad.
The one on the right is a bit warped, as we bent the mould while we smooshed the green stuff into a relatively deep pocket on it. Using a bit more Polymorph would have made the moulds a bit thicker, and given extra support around the areas where the walls of the mould get a bit thin (and are liable to bending under any amount of pressure). But, for a first go, it didn't look too bad at all!

We compared our efforts to some resin-case bases to see how they lined up. After spraying the green and resin bases a uniform black it was tricky to tell them apart.


A heavy drybrush with gun-metal, followed by a lighter coat of shining silver acrylic paints, and the bases are ready for the miniatures to be mounted onto them.


If you look closely you can tell which are the resin cast bases, and which are the home-made ones. But you have to look twice to tell them apart. And for that reason, we're going to call these a success.

From the top-most base, running left-to-right, top-to-bottom, we have: homemade, resin, homemade. On the next line, resin, homemade, homemade and the base at the bottom of the photo is a professionally produced, resin-cast one.

A few hours work, some left over junk (that would otherwise have gone to landfill) and a bit of putty, and we've got some really nice bases for our playing pieces.

And the best part?
The polymorph is completely reusable. Once we've finished casting these industrial style bases for our space-soldiers and starship inhabitants, the plastic can be dropped into hot water, and used to create a whole new set of base moulds, in a completely different genre. That's the best £8 we've spent this month!

Monday, 13 October 2014

Another atmega128 gotcha - PORTF

After spending quite some time over the weekend messing about with these atmega chips, and having spent most of the day debugging frustrating php errors (it's such a cobbled mess of a language - so similar, yet ever so slightly different to other c-based languages, using a mix of c-style objects and basic-style string functions, it's very easy to get the exact syntax mixed up!) it was almost a relief to get back to sticking wires into things this evening.

Being a staunch advocate of VB6 (the cool version of Visual Basic, that - still - works on all versions of Windows, not that crappy bloatware .NET version) it didn't take long to knock up a simple test interface for our atmega128 chip.

The idea is that the chip monitors a number of pins and, when the state of any pin changes, the pin and direction (on/off) is reported back to the host over serial. It's a pretty simple concept - but one that's kept us occupied for a few hours this evening.

So when a pin goes low (we're using the internal pull-ups on the input pins to avoid having them floating around) we send a serial message, decoding the pin that triggered the message into an index number. When a pin goes high, the same message is sent, this time with the leading bit set to one, to indicate that the pin has toggled.

Our VB app simply ticks a tick box each time a particular index is received.
On box for "on" and one box for "off".
In each square, once both "on" and "off" messages have been received, the square turns green. After initial testing, we had this:


All of the boxes, bar the top four had successfully received an "on" and an "off" message. Of the red boxes remaining, none had received either an or or off message. It looked very much like the change of pin state wasn't being registered.

The first thing, of course, is to test continuity on all the soldering joints. Surface mount soldering the atmega128 chips isn't exactly difficult, but it's possible that one or more pins haven't actually joined properly to the pads they're sat on.


The test rig looks a bit shonky, but the wires have been rammed home firmly and there's no way they're coming out! So maybe it's the soldering on the other side?


Ok, the soldering isn't the prettiest, but it's functional - all the pins on the microcontroller have continuity to the connectors around the edges of the board (all except pin 38 which etched badly and came away, but we're not using that pin in our code).

The problem doesn't seem to be the hardware, so it must be the code.
After spending a good couple of hours re-writing the code - to effectively do the same job, but using a slightly different method - the problem remained. After tracing back the non-responsive pins to our PCB, and in turn tracing those back to the atmega128 pinout, it turns out that it's half of PORTF that's not responding.

A quick Google for "atmega128 portf problem" and one of the top answers was this page: http://www.avrfreaks.net/forum/problem-portf-and-portg-atmega128



"And of course if you try and use portf 4-7 you need to turn off the JTAG fuse."

That looks like it!
Caught out by another atmega128 gotcha.
Disabling the JTAG fuses and re-programming the chip, and the results are much more encouraging;


Our first full successful board test.
So far, we've been bridging the empty board contacts where the hall sensors will go (there's no point soldering 48 sot-23 sized hall sensors onto a board if it's badly designed and won't work from the outset!). After tonight's successful test, we'll be able to hook up the hall sensors and confirm the final board design, in order to get the gerbers posted off to a fabrication house for manufacture.

If anyone else has problems with PORTF on an atmega128 in future, check your JTAG fuses (and disable them if necessary!)