donderdag 5 mei 2016

Olympus Air Nexus 6 case

I got an Olympus Air for my birthday, but my Nexus 6 only fits barely in the default hook that comes with it. Fortunately the hardware specification is available online which have sample files for creating a case for the iPhone. Kevin McCormack created a Nexus 6 case and graciously shared it on the thingiverse. I was able to combine the two and create a new model, which I uploaded to Shapeways where it is now available if you wish to print one (the "Buy" option is "at cost", I have no shop there and will not make a profit from purchases). You can also download the model if you wish to modify it or print it yourself.
The first time the case was slightly too large, possibly due to the original being meant for different materials. After measurements I reduced the model by 2mm, 1mm and 0.4mm and the result turned out reasonably well. Purple was a better color for me too.

I haven't yet tried it, but the Olympus Air fits well, and the new case is slightly smaller. The phone fits well in it, and doesn't move.




donderdag 17 oktober 2013

Old FPGA project


At one point, quite some time ago, I played with my Spartan FPGA. I'm reminded of this because the font was similar to the font I'm using for my new phone. I only found this one screen shot (the blog at the time is gone). As you can see some parts came out pretty nicely, though.

woensdag 16 oktober 2013

Proportional fonts

In this post I added the code so far. It basically has the control for the LCD display (some of it came from the original sample code, although I changed quite a few pieces to be more readable/faster/more efficient) and connects the output of the SIM900 to the display (although everything is still copied to the PC using the hardware serial port as well). Once I have an input device on my phone this connection will be severed, which hopefully will return the 4k memory currently in use for the software serial connection (as I can switch the SIM900 to use the hardware UART). The part that changed compared to yesterday is highlighted in yellow. Basically, when the left side of a font is empty, I shift the character one to the left (which is fine, because there is always a single pixel between characters). If there are one, two or three columns to the right of the font empty, I will reduce the width of the character accordingly. If the bottom row is empty, and it is a character, I shift the character one down (which is fine, because there is always a single pixel below characters). This means a lot more characters might fit on a line, if they are of the narrow variety (e.g. 'i' and 'l').

// Maarten's phone.
// Copyright 2013, Maarten Hofman.

#include

byte Font[] = {
  0x00, 0x00, //
  0x09, 0x10, // !
  0x05, 0x00, // "
  0xC5, 0x86, // #
  0xDA, 0xA8, // $
  0x85, 0x52, // %
  0xC3, 0x76, // &
  0x04, 0x00, // '
  0x04, 0xC4, // (
  0x41, 0x12, // )
  0xD5, 0xD6, // *
  0xD0, 0x80, // +
  0x00, 0x12, // ,
  0xC0, 0x80, // -
  0x00, 0x10, // .
  0x84, 0x12, // /
  0xAA, 0x29, // 0
  0x24, 0x48, // 1
  0xE2, 0xF1, // 2
  0xA2, 0xE8, // 3
  0xE8, 0xC8, // 4
  0xCA, 0xB8, // 5
  0xCA, 0xA9, // 6
  0xA2, 0xC8, // 7
  0xEA, 0xA9, // 8
  0xEA, 0xC8, // 9
  0x10, 0x00, // :
  0x10, 0x12, // ;
  0x84, 0x44, // <
  0xC0, 0xA0, // =
  0x81, 0x12, // >
  0xE2, 0x90, // ?
  0x2A, 0xE1, // @
  0xEA, 0xD9, // A
  0xEB, 0xB9, // B
  0x0A, 0x21, // C
  0x2B, 0x39, // D
  0xCA, 0x31, // E
  0xCA, 0x11, // F 
  0x0A, 0xA9, // G
  0xE8, 0xD9, // H
  0x92, 0x20, // I
  0x20, 0x38, // J
  0xCC, 0x55, // K
  0x08, 0x71, // L  
  0xAD, 0x59, // M
  0xA9, 0x5D, // N
  0x2A, 0x29, // O
  0xEA, 0x91, // P
  0xAA, 0x6D, // Q
  0xEA, 0xD5, // R
  0xCA, 0xA8, // S
  0x92, 0x00, // T
  0x28, 0x29, // U
  0x45, 0xA6, // V
  0xB8, 0x79, // W
  0x85, 0x56, // X
  0x95, 0x00, // Y
  0x86, 0x72, // Z
  0x0B, 0x31, // [
  0x81, 0x44, //
  0x26, 0x68, // ]
  0x2A, 0x00, // ^
  0x00, 0x70, // _
  0x01, 0x00, // `
  0xE2, 0xE9, // a
  0xC8, 0xB9, // b
  0xC0, 0xA1, // c
  0xE0, 0xE9, // d
  0xEA, 0xA1, // e
  0x4A, 0x11, // f 
  0xEA, 0xB8, // g
  0xC8, 0xD9, // h
  0x01, 0x11, // i
  0x04, 0x28, // j
  0xC8, 0xD5, // k
  0x08, 0x11, // l  
  0xD0, 0xD9, // m
  0xC0, 0xD9, // n
  0xC0, 0xA9, // o
  0xEA, 0x81, // p
  0xEA, 0xC8, // q
  0xC0, 0x91, // r
  0x84, 0xA8, // s
  0x48, 0x21, // t
  0x00, 0x79, // u
  0x00, 0x29, // v
  0x90, 0x29, // w
  0x85, 0x06, // x
  0xE8, 0xA8, // y
  0xC0, 0xE4, // z
  0x4B, 0x21, // {
  0x90, 0x00, // |
  0x26, 0xA8, // }
  0xAD, 0x00, // ~
  0xFF, 0xFF, // DEL
};

/* 4-bit serial communication LDS183 LCD module */
const int SCOMMAND = 0;
const int SDATA = 1;

#define DC PIND4
#define CS PIND2
#define SDA PIND6
#define RESET PIND3
#define CLK PIND5

#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))

byte gr = 255;
byte gg = 255;
byte gb = 255;

SoftwareSerial GPRS(7, 8);
unsigned char buffer[64]; // buffer array for data receive over serial port
int count = 0;     // counter for buffer array 

byte cursor_x = 0;
byte cursor_y = 0;

void setup () {
  DDRD = 0x7C;  
  lcdInit();
  GPRS.begin(9600);               // the GPRS baud rate   
  Serial.begin(9600);             // the Serial port of Arduino baud rate.
}

void loop() {
  int i, j, k, l;
  setXY(0, 0, 128, 128); // Initial screen size    
  for (i = 0; i < 128 * 128; i++) { // black background
      clearPixel();
  }
  setColor(255, 255, 255);
  printString("Goliath online...\n");
  //printString("!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~");

  while (true) {  
    if (GPRS.available()) {
      // if date is comming from softwareserial port ==> data is comming from gprs shield
      while(GPRS.available()) {
        // reading data into char array 
        byte in = GPRS.read();
        buffer[count++] = in;
        printChar(in);
        // writing data into array
        if(count == 64) break;
      }
      // if no data transmission ends, write buffer to hardware serial port
      Serial.write(buffer, count);      
      // set counter of while loop to zero
      count = 0;
    }
    if (Serial.available())            // if data is available on hardwareserial port ==> data is comming from PC or notebook
      GPRS.write(Serial.read());       // write it to the GPRS shield
  }
}

void lcdInit() {
  cbi(PORTD, CS);
  cbi(PORTD, SDA);
  sbi(PORTD, CLK);
  sbi(PORTD, RESET);
  cbi(PORTD, RESET);
  sbi(PORTD, RESET);
  sbi(PORTD, CLK);
  sbi(PORTD, SDA);
  sbi(PORTD, CLK);
  delay(10);
  sendCMD(0x01); // Software Reset
  // Write Contrast
  sendCMD(0x25);
  sendData(64);
  //Sleep Out and booster on
  sendCMD(0x11);
  delay(10);
  // Display Inversion off
  sendCMD(0x20);
  // Idle Mode off
  sendCMD(0x38);
  // Display on
  sendCMD(0x29);
  // Normal Mode on
  sendCMD(0x13);
  // Memory Data Access control
  sendCMD(0x36);
  sendData(0x60);
  sendCMD(0x3A);
  sendData(5);   //16-Bit per Pixel
  // Frame Frequency Select
  sendCMD(0xB4);
  sendData(0x03);
  sendData(0x08);
  sendData(0x0b);
  sendData(0x0e);
  // Display Control
  sendCMD(0xBA);
  sendData(0x07);
  sendData(0x0D);
}

void shiftBits(byte b, int dc) {
  cbi(PORTD, CLK);
  if ((b&128)!=0) sbi(PORTD, SDA); else cbi(PORTD, SDA);
  sbi(PORTD, CLK);
  cbi(PORTD, CLK);
  if ((b&64)!=0) sbi(PORTD, SDA); else cbi(PORTD, SDA);
  sbi(PORTD, CLK);
  cbi(PORTD, CLK);
  if ((b&32)!=0) sbi(PORTD, SDA); else cbi(PORTD, SDA);
  sbi(PORTD, CLK);
  cbi(PORTD, CLK);
  if ((b&16)!=0) sbi(PORTD, SDA); else cbi(PORTD, SDA);
  sbi(PORTD, CLK);
  cbi(PORTD, CLK);
  if ((b&8)!=0) sbi(PORTD, SDA); else cbi(PORTD, SDA);
  sbi(PORTD, CLK);
  cbi(PORTD, CLK);
  if ((b&4)!=0) sbi(PORTD, SDA); else cbi(PORTD, SDA);
  sbi(PORTD, CLK);
  cbi(PORTD, CLK);
  if ((b&2)!=0) sbi(PORTD, SDA); else cbi(PORTD, SDA);
  sbi(PORTD, CLK);
  cbi(PORTD, CLK);
  if ((b&1)!=0) sbi(PORTD, SDA); else cbi(PORTD, SDA);
  if (dc == SDATA) sbi(PORTD, DC); else cbi(PORTD, DC);
  sbi(PORTD, CLK);
}

//send data
inline void sendData(byte data) {
  shiftBits(data, SDATA);
}

//send cmd
inline void sendCMD(byte data) {
  shiftBits(data, SCOMMAND);
}

// Defines the area in which the pixels will be written, (x,y) is the top left, (dx,dy) is the size.
void setXY(byte x, byte y, byte dx, byte dy) {
  sendCMD(0x2A);
  sendData(x);
  sendData(x+dx-1);
  sendCMD(0x2B);
  sendData(y);
  sendData(y+dy-1);
  sendCMD(0x2C);
}

//converts a 3*8Bit-RGB-Pixel to the 2-Byte-RGBRGB 565 Format of the Display
inline void setPixel(byte r, byte g, byte b) {
   sendData((r & 248) | g >> 5);
   sendData((g & 7) << 5 | b >> 3);
}

inline void setPixel() {
  setPixel(gr, gg, gb);
}

inline void clearPixel() {
  setPixel(0, 0, 0);
}

void printString(char *st) {
  int stl, i;
  
  stl = strlen(st);
  
  for (i=0; i
    printChar(*st++);
}

void newLine() {
  cursor_x = 0;
  cursor_y += 6;
  if (cursor_y >= 122) {
    cursor_y = 0;
  }
  setXY(0, cursor_y, 128, 6);
  for (int i = 0; i < 128 * 6; i++) clearPixel();
}

void printChar(byte c) {
  if (c < 32) {
    if (c == 10) newLine();
  } else {
    cursor_x += printChar(c, cursor_x, cursor_y);
  }
  if (cursor_x > 122) newLine();
}

// Returns the width of the character.
byte printChar(byte c, byte x, byte y) {
  byte p1, p2;
  int iFont;
  byte width = 6;
  
  iFont = (c - ' ') * 2;
  if ((iFont < 0) || (iFont > 254)) iFont = 254;
  p1 = Font[iFont];
  p2 = Font[iFont+1];
  
  // Handle proportional fonts.
  if ((p1 & 9) + (p2 & 17) == 0) {
    width--;
    x--;
  }
  if ((p1 & 36) + (p2 & 72) == 0) {
    width--;
    if ((p1 & 6) + (p2 & 164) == 0) {
      width--;
      if ((p1 & 146) + (p2 & 32) == 0) width--;
    }
  }
  if ((c & 64) && ((p2 & 112) == 0)) y++;
  
  setXY(x, y, 5, 5);
  // Line 1
  if (p1 & 1) setPixel(); else clearPixel();
  if (p1 & 2) {
    setPixel();
    setPixel();
    setPixel();
  } else {
    clearPixel();
    clearPixel();
    clearPixel();
  }
  if (p1 & 4) setPixel(); else clearPixel();
  // Line 2
  if (p1 & 8) setPixel(); else clearPixel();
  if (p1 & 1) setPixel(); else clearPixel();
  if (p1 & 16) setPixel(); else clearPixel();
  if (p1 & 4) setPixel(); else clearPixel();
  if (p1 & 32) setPixel(); else clearPixel();
  // Line 3
  if (p1 & 8) setPixel(); else clearPixel();
  if (p1 & 64) setPixel(); else clearPixel();
  if (p1 & 128) setPixel(); else clearPixel();
  if (p2 & 128) setPixel(); else clearPixel();
  if (p1 & 32) setPixel(); else clearPixel();
  // Line 4
  if (p2 & 1) setPixel(); else clearPixel();
  if (p2 & 2) setPixel(); else clearPixel();
  if (p1 & 16) setPixel(); else clearPixel();
  if (p2 & 4) setPixel(); else clearPixel();
  if (p2 & 8) setPixel(); else clearPixel();
  // Line 5
  if (p2 & 16) setPixel(); else clearPixel();
  if (p2 & 32) {
    setPixel();
    setPixel();
    setPixel();
  } else {
    clearPixel();
    clearPixel();
    clearPixel();
  }
  if (p2 & 64) setPixel(); else clearPixel();
  return width;
}

void setColor(byte r, byte g, byte b) {
  gr = r;
  gg = g;
  gb = b;
}

dinsdag 15 oktober 2013

Cell Phone

It has been over a year since my last post, and you possibly think that I am idle. However, this isn't exactly true. After I got a 128x128 pixel 64k color display for Arduino, which I soldered together, I bought the Arduino as well and then wondered what my next project would be. Meandering through the mall I found a very inexpensive SIM900 GRPS chip, which worked well together with the other components I had. So I started building a cell phone, which currently is still in a preliminary stage, of which there are a few pictures here.


As you can see, the cell phone isn't exactly small (and this is without the microphone, speaker and input device) but it works a little bit (if you read the display correctly it says "Call ready" at the end). It's currently using 8 KB out of 32 KB, so I think I should be able to write at least a few services for it. It can do SMS, calls and even internet with a speed up to 86 kb/s (more than enough to download pictures to fill the display). I'm using an H2O subscription, which is very cheap ($10 for the SIM, and $10/90 days, unless I use a lot of money on calls ($0.05/minute), SMS ($0.10/message) or Internet ($0.10/MB). I'm still not entirely sure about the input mechanism, I'm considering a PS/2 keyboard, but will probably end up with up, down, left, right and submit as an interface.

maandag 13 augustus 2012

The tweeting cat IV


The system is now finished. The full version displayed above, and the twitter stream is available at NephthysCat. Of course she's not wearing the harness all the time so don't expect constant updates. The battery life is longer than the calculated 7.1 hours, which was to be expected because I didn't expect the components to run at their maximum rating the whole time.

I solved the charging of the LiPoly battery by cutting a USB cable in half and using that power for the input of the charger circuit. On the Playstation 2 I have the cu command (cu -s 9600 -l /dev/ttyUSB0 dir >entry) run in a local terminal. This will cause all the incoming messages to appear in the file "entry":

4F0088E27B5E
4F00889B0F53
4F00890403C1
4F00890403C1
4F00889BD78B
4F0088DC2932
4F0088DC2932
4F0088DC2932
4F0088DC2932
4F0088DC2932
4F0088DC2932
4F0088DC2932
4F0088DC2932
4F0088DC2932
4F0088DC2932
4F0088DC2932
4F0088DC2932
4F0088DC2932
4F0088EE88A1
4F0088EE88A1
4F0088EE88A1

A simple pike program keeps track of which line we are in this file, and continues from there. If an entry gets added beyond that, it will a message read from a file called "tweets". Here is the code:

/* NEPH.PIKE - Allow Nephthys to tweet */
/* 110812: Started programming */

#pike 7.0

#include

mapping(string:string) tweets = ([]);

int main() {
  string data = Stdio.read_file("tweets");
  string *lines = data/"\n";
  for(int i=0;i
    tweets["0"+lines[i]]=lines[i+1];
    tweets["1"+lines[i]]=lines[i+2];
  }
  string current = Stdio.read_file("current");
  int where = (int)current;
  write("Last index: " + where + "\n");
  int count = 0;
  while(1) {
    string entries = Stdio.read_file("entry");
    lines = entries/"\n";
    if((sizeof(lines)-1)>where) {
      tweet(lines[where++]);
      Stdio.write_file("current", "" + where);
      write("New index: " + where + "\n");
      count = 0;
    } else {
      if (count++ > 60) {
        //tweet("Purr!");
      }
    }
    write("Sleeping...\n");
    sleep(60);
  }
}

void tweet(string text) {
  text-="\r";
  text-="\n";
  string last = text[(sizeof(text)-2)..(sizeof(text)-1)];
  string code = "" + random(2) + last;
  string message = tweets[code];
  if (!message) {
    message = "Cat error " + code;
  }
  write("Tweeting: " + message + "\n");
  system("/usr/local/bin/ttyter -keyf=nephthys -status='" + message + "'");
}


The code doesn't filter out duplicates, but fortunately twitter does this for you. It uses the ttyter command line to send the tweets. The tweets file contains the last two digits of the tag, followed by two possible tweet lines which it can choose from at random. For example:

32
Nom, nom, nom!
Oops, I dropped a piece of food. No worries, Miuty will get it.
A1
This is smelly tasty food from the top shelf.
This food is better because it is higher.

The tags are placed at crucial positions in the house, for example near the cat toy.


There are obviously many improvements that can be added, and more tags that can be spread around the house.

vrijdag 10 augustus 2012

The Tweeting cat III


The new version uses a LiPoly battery, which outputs 3.7V. Since the 33V requires 4.6V input, I left that circuit untouched, and instead moved the 3.7V from the LiPoly battery into a 5V DC to DC Step Up circuit. The resulting circuit is a lot smaller (as you can see in the picture). Initially there was a problem with the circuit not working, but it turns out that there is an open (and unsoldered) jumper that disables the entire thing. After moving the battery connector to this jumper and adding a ground wire the thing worked fine.

I hooked it up and it also notified the laptop when Nephthys was eating, I even got three messages, each time she lowered her head to the food bowl. The next step is to write the playstation 2 code, where I'm planning to just have cu (the terminal emulator) running in the background writing to a file, which the pike application will read from and send messages to ttyter which sends them to twitter. I decided to stick with cu, because despite my device driver coding the USB system is still quite unstable, and opening and closing the port repeatedly (which will happen if I start writing my program for it) could cause it to crash.

Another aspect is charging the LiPoly battery, I have a circuit for that but it gets rather hot (mostly because my input voltage is 12V). I'm looking for a more efficient input, but probably will use a USB port for this.

maandag 6 augustus 2012

The tweeting cat II

I managed to get the XBee USB explorer hooked up to my playstation 2 linux system, and it only crashed twice so far. Playstation 2 linux runs 2.2.1 (a very old release). Since 2.2.1 doesn't have USB and the Playstation 2 uses USB for the keyboard and mouse, they ported the 2.2.18 USB modules, but the driver for the XBee USB explorer only exists in 2.2.19 and up, so I had to port that back too. It required me to manually edit usbserial.c (as this contains PS2 specific code) but the other files could just be copied from 2.2.20 and 2.2.19.

The next step was to build the circuit. I saw a good option on bildr blog which I decided to persue:
I also added a 330 Ohm resistor and a LED to the ID-12, to make sure you could see when a tag was successfully scanned. The positive lead of the LED goes to the pin, for those that are wondering. As a battery supply I used four rechargeable NiMH AAA batteries, which give a voltage between 5.6 and 4.9 volt, I initially used a diode to drop the voltage by 0.7V to ensure that I didn't send more than 5.4V to the ID-12 (the XBee is protected by the 3.3V converter).

Then I had to attach it to a cat harness, for which I used electrical tape and band aid tape (to not bother the skin of the cat with the electronics). This is what Nephthys looks like with the system on:


You can see the LED and the RFID-12 on this picture.


And on this picture you can see the battery pack with the XBee attached to the back.

The system worked once, but the cat decided not to go after any of the RFID tags I put around the house and the system stopped working after an hour. After a lot of debugging I discovered this was because the batteries had run out of power (oddly enough the RFID-12 and the XBee still work, and the LED still blinks, but the RFID-12 refuses to send any data to the XBee, which made for a lot of trouble debugging. So I'll try the system without a diode next, which would give the system a lot more battery life.

As you can imagine, I'll put tags on the food bowls, the water bowls, the favorite sitting spots of the cat, maybe the litter box, but also the other cat, Miuty:


Of course, I'll also have to set up a twitter account and create a way to translate the RFID numbers into tweets. But it will all happen.