maandag 7 februari 2022

Pocket Operator (PO-32) to Arturia Microbrute sync

Two birthdays ago I received an Arturia Microbrute which I did very little with so far. Instead I worked on a system combining a Yamaha CS, the Pocket Operator PO-32 by teenage engineering and a TASCAM digital recorder/mixer. However, lately I've become interested in Eurorack modular synthesizers and I discovered that the Arturia Microbrute has some (quite limited) patch options. I also became curious whether it would be possible to include the PO-32 into the modular system and discovered that it has a sync signal, both going in and out and I was curious if I could use this to have the PO-32 control the Arturia Microbrute.

The Internet has quite a few resources here, most of which have the Arturia Microbrute's Gate Out connected to the sync in from the pocket operator (e.g. https://www.youtube.com/watch?v=WIobsEA-_cA). Although this seems to work, if you research it closer there are some warnings here. The Arturia Microbrute's Gate Out is rated 10V peak-to-peak, whereas the pocket operator manual states that the sync signal should not exceed 5V peak-to-peak. From all the youtube videos on this it seems it usually works, and definitely doesn't destroy the pocket operator, but there are reports of the pocket operator freezing at certain battery levels. I suspect this all can be easily voided with a resistor network, and I'll work on that soon. However, I was more curious whether it was possible to have the Microbrute get the sync signal from the Pocket Operator through the Gate In.

Unfortunately the Gate In of the Microbrute expects a 5V peak-to-peak minimum (I suspect it can handle up to 12V) and the Pocket Operator seems to output not more than 1V, as shown on p0k3t0's blog. As you know, I faced a problem like this in the past with the RS-232 conversion for the PIC microcontroller, and I thought that I could use the same circuit here. Unfortunately the RS-232 conversion also required an invert, which is not useful here, so I decided to link two together to form the following circuit:

I'm sure a smaller version is possible, but as shown it is very lenient, it's very accepting to different voltages, and the output's voltage is basically whatever voltage you run the circuit at. 4.5V seems to be enough for the Arturia Microbrute, though.

 

Here is a picture of the working prototype I made. I can make a video later.

I used circuitlab to make this circuit diagram, also because it allows you to simulate the result:

It shows that a 0V input voltage results in a 0V output, and a 1V input results in a 4.5V output. When you change the voltage of the circuit to 12V it will adjust accordingly.

The simulator also allows me to measure current usage, which is 2.3mA, which seems acceptable.


zondag 22 april 2018

Updated source for SERVO.ASM

Will Collier has updated the source code for my servo controller (described in my post here). It should now be able to reach its full range.

maandag 9 april 2018

PS/2 keyboard to MIDI translation

In 2011 I made a PS/2 keyboard to MIDI converter. It came with a sample source code, but this source code had some issues as it was originally meant for a regular keyboard read, not one that is MIDI specific. So at the encouragement of Oscar I wrote a better version of the source code that has the following additional features:

  • It allows mapping of every key on the keyboard using two tables, one for the regular keys (BaseKeyboard) and one for the keys whose scan codes start with 0xE0 (SuperfluousKeyboard). Setting a key is as simple as giving it a note (1-127).
  • It removes the auto repeat that some keyboards have that could create notes with an annoying echo.
  • It allows assigning of MIDI CC messages. This is done by adding 128 to the channel you wish the CC message to be sent over. By default it will send key presses with 127 and releases with 0. This can be changed globally, but not per CC.
  • It has various fixes that make the code run smoother and be less error prone.
The source code is available here. Remember to keep the spacing intact, as otherwise the file may not compile. It is intended for a PIC16F690. The wiring is described at the top, and in the original article.

The biggest flaw so far is that upon startup it sends a few random MIDI sequences out, because the keyboard sends its start up sequence to the PICmicro. This can be avoided by mapping those specific keys to 0, or by adding a startup delay.

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.