Finally! I've figured out what's going on here.
First off, from Laipac TLP/RLP 315mhz - SparkFun Electronics Forum
The HT12E encoder chip used in Laipac's datasheet uses a different type of encoding. For Zeros it outputs "001" and for Ones it outputs "011". With this encoding (after you are syncronized), your receiver will measure how long the signal is high to determine what bit was sent.
This would explain why the "high" not as long as "low" when transmitting a square wave.
So, some modifications were made to the transmitter code to only use the time "high" for meaningful data. This means the data rate will be variable depending on how many 1s.
int dlyms = 208;
void xmitByte(byte vData) {
byte bitv = 128;
for (int bitc = 0; bitc < 8; bitc++) {
if (vData & bitv) {
digitalWrite(xmitpin, HIGH);
delayMicroseconds(dlyms*2);
digitalWrite(xmitpin, LOW);
delayMicroseconds(dlyms);
}
else {
digitalWrite(xmitpin, HIGH);
delayMicroseconds(dlyms);
digitalWrite(xmitpin, LOW);
delayMicroseconds(dlyms);
}
bitv = bitv >> 1;
}
}
So a zero is high 208us low 208us, and a one is high 416us low 208us.
Example usage with corresponding preamble:
for (int xbr = 0; xbr < 3; xbr++) xmitByte(0);
xmitByte(150);
xmitByte(curInt >> 8);
xmitByte(curInt & 0xFF);
xmitByte(~curInt >> 8);
xmitByte(~curInt & 0xFF);
xmitByte(0);
The preamble is: 0x00 0x00 0x00 0x96. The 96 in binary is 01101001. As you can see the transmission includes the MSByte & LSByte of the data followed by its inverse. This will be used for error checking later.
On the receiving end:
// copyleft 2008 by Derek Yerger
byte amlvl = 15; // minimum timeout the routine will use in "clock recovery"
int waitOnlvl(byte vLvl) {
byte wlvl = digitalRead(7);
byte wlvl2 = wlvl;
int lvlcnt;
while (wlvl2 != vLvl) {
lvlcnt = 0;
while (wlvl2 == wlvl) {
wlvl2 = digitalRead(7);
lvlcnt++;
}
wlvl = wlvl2;
delayMicroseconds(dbg[1]*2);
}
return(lvlcnt);
}
unsigned int recvInt(void) {
// check for level change
unsigned long starttime = millis();
int tmocyc = 0;
digitalWrite(ledPin, HIGH);
byte reftm = amlvl + 20;
byte reftm2 = reftm + (reftm / 2);
//read potential bits
unsigned long curdata = 0;
unsigned int preamble = 0;
byte lvlq = 0;
byte cbit = 0;
byte abc = 0;
waitOnlvl(0);
waitOnlvl(1);
while ((preamble != 150) && (tmocyc < 5000)) {
preamble = (preamble << 1) + (curdata >> 31);
curdata = curdata << 1;
cbit = waitOnlvl(0);
if ((cbit < reftm) && (cbit > amlvl)) {
reftm = cbit;
reftm2 = reftm + (reftm / 2);
}
if (cbit > reftm2) curdata++;
waitOnlvl(1);
tmocyc++;
if ((tmocyc % 128) == 127) {
if (millis() - starttime > 1000) tmocyc = 5000;
}
}
if ((preamble == 150) && ((curdata >> 16) == ~(curdata & 0xFFFF))) {
digitalWrite(ledPin, LOW);
return(curdata & 0xFFFF);
}
}
Here the routine shifts data in, shifting the MSB of curdata into preamble. When preamble is 150 the loop exits. This means two bytes (0x00, 0x96) need to shift into preamble before the loop exits, or until 1000 ms has elapsed. The tmocyc var is to avoid calling millis() too often.
Once the loop exits, the preamble is checked, and the data in curdata compared for the integer and its inverse. If this matches up the function returns the number.
I've tested this with a counter and it is working great. The proper data comes out, and no erroneous data is returned either. I hope this helps all of you in the same situation I was a week ago. Please keep this thread updated with any libraries that come of this. Also, I'm sure there are minor improvements which could be made to make the code smaller.