SparkFun Forums 

Where electronics enthusiasts find answers.

General project discussion / help
Did you make a robotic coffee pot which implements HTCPCP and decafs unauthorized users? Show it off here!
By Mee_n_Mac
#166306
waltr wrote:Lets further clarify what you have done and what are trying to do:
1- You can read in the serial data steam into bytes. yes?
To be clear my understanding is that he has a logic analyzer to capture the bitstream. He 'manually' translates (his wording) the waveform into the 71 bits (not 77, a typo ?) he lists. I assume he does this by looking at the LA output, not unlike what I did in my mark-up off his trace. That's certainly a PITA and error prone. So I assume that's step #1, and the trickiest part. Replacing the LA and human effort w/an Arduino, certainly a do-able thing.

Parsing the bits into the bytes desired is then trivial.

Lets see if the OP concurs.
By Sparky99
#166307
Trying to answer your questions I cannot get the data to be recognised by the aurdino I have played with various settings Baud rates but nothing happens.
Re questions above.
1) With a logig analyser i can read the serial data (dont know what into bytes refers)
2 & 3) the end result is to get the pc to display the uncoaded data as 6 pairs of hex numbers
4) not sure
5) converting hex to ascii is not the problem

Question: Is it that you can not get the arduino to read the serial data stream correctly?
I cannot get the arduino to read it at all.
Levels are 5v and OV
There may be an extra 1 in the preamble, i dont start manually deciphering until i get to the first 01
By waltr
#166309
I have played with various settings Baud rates but nothing happens.
I cannot get the arduino to read it at all.
Still have questions on these two statements.

Baud rate? Are you trying to use the aurdino's UART? or Softserial? These will not work since the serial data stream is not asynchronous serial. You MUST use a custom Bit-bang serial receiving code like how code for an IR remote receiver works.
With the serial data you have Baud Rate is a meaningless term. A proper term would be either 'clock rate' or 'sampling rate'.
So, you need to write code that samples the input pin at a known period of either a multiple of the data rate or at the data rate. Since you have a logic analyzer have the code output what it samples. Then connect the analyzer to both the input and output. These should be the same, with a slight delay of the output. Then the aurdino's code is properly sample the input data. Next is having the code turn the input sampling into '1s' and '0s' and shifting these into bytes (8bit chunks) and storing into a buffer. After that code pulls the data from the buffer, converts it to ASCII and sends it to the PC.
By jremington
#166310
Perhaps this example will give you some ideas about how to proceed. The following is part of some C code I wrote for a PIC processor to decode the bitstream from a weather station remote sensor.

The Arduino code can look similar.

Basically the program waits for a pulse edge and then times the next pulse transitions. First it looks for a series of "0" pulses followed by a "1" (the preamble), then decodes the message. A long pulse is a binary one and a short pulse is a binary zero. In your case, the bit times are constant and you would be looking for an absence of a transition during the bit period to distinguish a 0 from a 1 (just as you do by hand). The code below uses a hardware timer, but on the Arduino you have the function micros() that does the same thing.

Code: Select all
// the following define expected interval timing and allowed 
// uncertainty for timer tick rate 64 uSec

#define deltaT 5
#define InRange(TVAL, TSETUP, dT) ((TVAL<=(TSETUP+dT))&&(TVAL>=(TSETUP-dT)))

#define TrfON0 31               // 2 ms RF ON time, zero bit 
#define TrfON1 94				// 6 ms RF ON time, one bit

BitValue = 0;
while(1)

   {  //look for message preamble
      
	  while ((PINB & RF_IN) == RF_ON); //RF is on, wait for RF off

      Ton = TCNT0;     // Read ON timer value
	  TCNT0 = 0;     // reset timer

	  while ((PINB & RF_IN) == RF_OFF); //time the off period 
      
      Toff = TCNT0;  
      TCNT0 = 0;

      if (InRange(Ton, TrfON0, deltaT) && InRange(Toff, TrfON1, deltaT)) PreambleCnt++;
	  if (InRange(Ton, TrfON1, deltaT) && InRange(Toff, TrfON0, deltaT)) BitValue=1;
	  if ((PreambleCnt > 6) && (BitValue == 1)) break;

// invalid RF on pulse length?

	  if (!InRange(Ton, TrfON0, deltaT) && !InRange(Ton, TrfON1, deltaT))
	  { PreambleCnt=0; BitValue=0; }

    } //end while (1)


    PORTB |= LED;  //LED on, preamble detected

// if OK so far, get rest of message

     BitCnt = 1;
	 SI_msg.l = 0; //ignore start bit, clear message buffer

/*
** loop through next 28 bits and build message
** Decide one/zero by checking the RF_ON length (2 or 6 ms)
*/
     while (BitCnt < 29)
     {  
         while ((PINB & RF_IN) == RF_ON); //wait for RF_OFF
		 
		 TimerValue = TCNT0;
		 TCNT0 = 0;
		 
		 BitValue = 2;  //assume invalid

		 if(InRange(TimerValue, TrfON0, deltaT)) BitValue=0;
		 if(InRange(TimerValue, TrfON1, deltaT)) BitValue=1;
 
 		 if(BitValue == 2) ErrCode |= 1; //invalid RF ON pulse width    
 
//shift in bit

		SI_msg.l <<= 1;
		if (BitValue == 1) SI_msg.l |= BitValue;

//wait for rising edge, with timeout upon valid end of message (RF_OFF >> 6 ms)

         while (((PINB & RF_IN) == RF_OFF) && ((TCNT0 & 0x80) != 0x80)); //0x80 = 8.2 ms

		 TimerValue = TCNT0;  // and check that RF off time was valid
		 TCNT0 = 0;

		 if (BitCnt < 28)  //unless it is the last bit, flag bad RF_OFF period
			{
			 if ((BitValue == 1) && !(InRange(TimerValue, TrfON0, deltaT))) ErrCode |= 2;
			 if ((BitValue == 0) && !(InRange(TimerValue, TrfON1, deltaT))) ErrCode |= 4;
			}

         BitCnt++; 

     } //end while BitCnt
By Mee_n_Mac
#166312
Again let me ask if the level stays at 5v after the end of a transmission and until the start of the next transmission ? That is the start of the bitstream is a falling edge, from 5v to 0v.

Alternately you should be able to use the pulsein() function to measure an array of 'high' pulse times. The first 8-10 of these can be used to set the bit width. This can then be used to count the number of 0's. Then, post-transmission, the bitstream couldvbe reconstructed.
http://arduino.cc/en/Reference/pulseIn
By Sparky99
#166313
hi ive attached a larger view of the data from the logic anaylyser it clearly shows the state of things before and after transmision.
You do not have the required permissions to view the files attached to this post.
By Sparky99
#166314
Attachment
trace4.png
You do not have the required permissions to view the files attached to this post.
By Mee_n_Mac
#166325
Good pic, it tells all.

I've marked it up as I did before, with a logic 1/0 determination at the end of each bit shown ... for the bits clocked. I've super-imposed a constant clock, as shown in red. Obviously some 0 bits at the tail end never get clocked and so must be assumed and added until the message length of 70 bits is reached.
(click on to opan and enlarge)
TelescopeBitstream2.jpg
My datastream from the above agrees w/yours. Yea!
1111111110 01 11101000 01 01100001 01 01110101 01 00111110 01 00010001 01 11110000
........................E8.............61............75.............3E.............11...............F0
You can also get a 'high' pulse count/measurement from the above bitstream. Time how long each logic level 1 pulse lasts btw the zero levels. Let's assume you measure a pulsewidth of 500 usec for all of the first few bits. You could then measure all the PWs in the stream and express them as a ratio to the 500 usec nominal PW. That's also shown above (2nd line for PWs > 1). Those normalized PWs, for the pic above, would be ;
11111111151113931933113335111157731111?? (the last PW (??) is the 'time out' PW for the pulseIn() function (>>> any normal PW).
It can be seen that for any measured PW ~= to a nominal PW, you should translate that into a bit = logic 1. For a longer measured PW, divide the measured PW by the nominal PW, round/truncate, subtract 1, then divide by 2. That result is the number of 0's you should have/append. Then append one logic 1 to the end of that. For example a measured PW = 9nPWs = (9-1)/2 = 4 zeros plus 1 one = 00001.

A sequence of PWs, normalized to a 500 usec PW, might be 1,1,1,3,9. That would be translated, using the above rules, from those normalized PWs into a datastream, = 11101000 01. That's your decoded E8 plus 2 bits of separator.

So now you need code to do the above ... and then some.
You do not have the required permissions to view the files attached to this post.
By Sparky99
#166326
Hi thanks for the help, im beginning to think its too complicated for me to tackle, I think i will try to find someone else to carry on with this project or forget it.
By jremington
#166337
Decoding that message is really a pretty simple task and ideal for the Arduino environment, but it would require access to the data stream for testing. If you post this problem on the very active Arduino forum http://forum.arduino.cc, you might even find someone in your area to help. The forum also has a "gigs and collaborations" section where you can hire help.
By Mee_n_Mac
#166351
Sparky99 wrote:Hi thanks for the help, im beginning to think its too complicated for me to tackle, I think i will try to find someone else to carry on with this project or forget it.
See if you (or your someone else) can follow the code below. It's written for an Uno-like Arduino, using an external interrupt (on pin 2) for the data input. It's one possible way to decode the message. I had thought that I could use the existing pulseIn() function to measure the bitstream's pulsewidths (PWs) and then get the 1's and 0's from those PWs, per my prior diagram (above). Turned out to be a bit more of a head-scratcher than I had estimated but still do-able. I did find out that the existing pulseIn() function doesn't timeout as I expected, so I had to make my own function to measure PWs. Perhaps the alternate technique of timing and clocking the bits in directly would have been easier afterall. :doh:

In any case the code is what it is. No doubt it could be made more efficient. There are validity checks and timing wrapover code I'd add to make it less error prone and more 'finished'. I did test it w/simulated measured PWs, those parts to turn PWs into printed hex data work. I've yet to check that my PW measuring code works as intended. I'm very unsure I can attach 2 ISRs as I've have. I am sure that could be fixed using a single ISR and a CHANGE condition. But if you have the telescope and an Uno ... :mrgreen:

EDIT: Why not just make it use a single ISR and avoid any problem ? DONE !

baseline, ver 0.1
Code: Select all

//declare the constants
const byte INpin = 2;     //input pin that receives signal from telescope
const int startPos[] = {
  12, 22, 32, 42, 52, 62};  //position of 1st data bits in zero indexed array
const unsigned long timeout = 20000;  //longest valid data pulse ~11 msec

//declare the variables
boolean printed = false;
byte temp = 0;
byte normPW[70];
byte bitstream[70];
byte pulseCNT = 0; 
byte bitCNT = 0;
byte databyte[6];
unsigned int loopCNT = 0;
unsigned long pulseWidth[70];
unsigned long avePW = 0;
volatile boolean running = false;
volatile boolean MSGincoming = false;
volatile unsigned long stime = 0;
volatile unsigned long PWmicros = 0;

void setup(){
  Serial.begin(9600);
  pinMode(INpin, INPUT_PULLUP);
  attachInterrupt(0, ISR_changing, CHANGING);
  reset();
}

void loop(){
  //measure the high PW
  //trap code in loop until end of transmission
  measurePW();

  //Exit the above while after pulse times out, end of message
  //now figure out the average pulsewidth
  //add up first 8 PWs
  if(printed == false){
    for(int i = 0; i < 8; i++){
      avePW += pulseWidth[i];
    }
    //now divide by 8 
    avePW = avePW / 8;
    avePW = avePW - 40;       //shorten avePW a little so truncation works

    //now construct an array of normalized PWs
    for(int i = 0; i < pulseCNT; i++){
      normPW[i] = pulseWidth[i]/avePW;  //stick with integer math
    }


    //now reconstruct bitstream content of 1's and 0's
    for(int i = 0; i < pulseCNT; i++){
      if(normPW[i] == 1){               //if normPW = 1, a logic 1 was sent
        bitstream[bitCNT] = 1;
        bitCNT ++;
      }
      else{                             //if normPW > 1, some # of 0's and one 1 was sent
        temp = (normPW[i]-1)/2;         //normPW = #0's x 2 + 1
        for(int z = 0; z < temp; z++){  
          bitstream[bitCNT] = 0;        //insert 0's into reconstructed bitstream
          bitCNT ++;
        }
        bitstream[bitCNT] = 1;          //add that one logic 1 after 0's
        bitCNT ++;
      }
    }

    //now pick out the desired data bytes and fill them
    //shift in a 1 or a 0
    for(int j = 0; j < 6; j++){
      for(int i = startPos[j]; i < (startPos[j] + 8); i++){
        if(bitstream[i] == 1){
          databyte[j] = databyte[j] | 0x01;
        }
        if(i != (startPos[j] + 7)){
          databyte[j] = databyte[j] << 1;
        }
      }
    }

    //now print the results to the serial monitor
    Serial.print("Message counter = ");
    Serial.println(loopCNT);
    for(int i = 0; i < 6; i++){
      Serial.print("Byte");
      Serial.print(i);
      Serial.print(" = 0x");
      Serial.println(databyte[i], HEX);
    }
    printed = true;
    //now reset everything for the next transmission
    reset();
    loopCNT++;
  }
}

void measurePW(){
  while(MSGincoming == true){
    if(INpin == LOW && running == true){
      delayMicroseconds(50);             //make sure ISR runs 1st
      pulseWidth[pulseCNT] = PWmicros;   //store PW in array
      pulseCNT ++;                       //increment counter
      running = false;                   //PW timer not running
    }
    if(INpin == HIGH){                   //look for stop bit, end of message
      if(micros() - stime > timeout){    //PW is longer than longest valid data
        MSGincoming = false;             //signal message rcvd, get out of while loop
        running = false;
      }
    }
  }
}

void reset(){
  for(int i = 0; i < 70; i++){
    normPW[i] = 0;
    bitstream[i] = 0;
    pulseWidth[i] = 0;
  }
  for(int i = 0; i < 6; i++){
    databyte[i] = 0;
  }
  pulseCNT = 0; 
  bitCNT = 0;
  avePW = 0;
  PWmicros = 0;
  temp = 0;
}

void ISR_changing(){
  if(INpin == HIGH){                  //this is rising edge logic
    //set timer flag
    running = true;
    //store pulse start time
    stime = micros();
  }
  else{                               //falling edge
    //set message being received flag
    MSGincoming = true;
    //calculate pulsewidth but not for 1st falling edge
    if(running == true){
      PWmicros = micros() - stime;
    }
  }
}
Comments, corrections welcomed.