Reading a digital signal - problems

I am trying to read a digital signal from my domotica system by using an arduino nano. I was able to read the signal without any problems using an atmega1284P and some bascom code. Now i would like to do the same using the arduino nano.

The signal (bittrain) i am trying to read starts with a long low pulse (between 3000 and 3200 microseconds long).
After that there is a signal of 22 bits. A short lowgoing signal is a 0 , a longer (more than 1000microseconds) is a 1
This signal is put 3 times on the domotica bus.

The routine is called from an interrupt activated in setup() : attachInterrupt(0, NB_get, RISING); (the domotica system is connected to pin 2)

The routine i try is:

unsigned long knop;
int NB_Bus_in = 2;

void NB_get() //starts when puls is going high
{ unsigned long puls;
  detachInterrupt(0);
  knop=0;
  do 
  {  puls = pulseIn(NB_Bus_in, LOW,3600);                    // wait until pin gets low, then time the low pulse
  }while (!((puls>3000)&&(puls<3200))&&!(puls==0)) ;         // loop until start pulse is passed,  is between300*10 and 320*10 long
  if (puls>0) // no error
  {  for (int NB_i=1;NB_i<=24;NB_i++)                        
     { puls = pulseIn(NB_Bus_in, LOW,3600);
        knop=knop << 1;
       if (puls>=1000&&puls!=0){knop|=0b1;}
    }
  } else goto einde;
 Serial.println(knop,HEX); //for testing the result
  einde:
  attachInterrupt(0, NB_get, RISING);
}

The problem is that is get different results back for the same signal and never the correct one.

Someone told me to try some other code:

byte NB_data[8]={0x1A,0xDD,0xC0};
byte NB_bytes;
#define TimeStart 3080
#define TimeZero  650
#define TimeOne   1450
#define TimeOff   3600

// check if the time was in range +/- 25%
#define IS_X(t,x) ((t > 3*(x)/4) && (t < 5*(x)/4) && (t!=0))
#define IS_S(t) IS_X(t,TimeStart)
#define IS_0(t) IS_X(t,TimeZero)
#define IS_1(t) IS_X(t,TimeOne)
#define IS_E(t) IS_X(t,TimeOff)

void NB_get() //starts when puls is going high
{ unsigned long puls;
  byte NB_i=0, NB_byte=0;
  NB_bytes=0;
 detachInterrupt(0);
  do 
  {  puls = pulseIn(NB_Bus_in, LOW,3600);                    // wait until pin gets low, then time the low pulse
  }while (!IS_S(puls)) ;         // loop until start pulse is passed
  if (puls>0) // no error
  {do   
    {  puls = pulseIn(NB_Bus_in, LOW,3600);
      if (IS_1(puls))NB_byte|=0b1;
      NB_byte=NB_byte << 1;
      if (NB_i==7) 
      {  Nikobus_data[NB_bytes++]=NB_byte;
         NB_byte=0; NB_i=0;
      } else NB_i++;
    } while (!IS_E(puls)) ;
  };
 attachInterrupt(0, NB_get, RISING);
  Serial.println(Nikobus_data[0]);
}

But this code simply crashes the arduino...
Can anyone help me to get this working?

The routine i try is

After a while, it gets boring asking people to post all their code, use auto format before doing so, don't call serial or delay in interrupt context, but I'm going to do it anyway.

love your patience though :grin: :grin: :grin:

Sorry, i didn't want to overflood the topic with the code. and its more than the forum "maximum allowed length (9500 characters)
Here it is: (the first code - second is all the same with the things from the second code added and the first subroutine removed)

#define I2C_ADDR 0x05
// could have several ardex devices with different i2c addresses! use this adress in Python script!

#define ArdEx5VoltIsOK false
// Only set this true if the ArdEx arduino connects to rPi via a hardware 3.3V - 5V level convertor
// This option permits 5V operation on the arduino. (Meduino device 3v/5v switchable was assumed.)

#include <Wire.h>
#include "Analog.h"

Analog analog;

int NB_Bus_in = 2;
int NB_Bus_out = 11;
int NB_Bus_start = 12;
unsigned long knop;
byte toest = 0;
unsigned long nbus;
String I2CResultaat;
byte Verbruikweten = 0;
char readyflag = 'X';

volatile char Bericht3[20];
volatile char opdrachttype;
int k;
int index = 0;
int readyflag2 = 0 ;

const int LEDpin = 13;

void setup ()
{
pinMode(NB_Bus_in, INPUT);
pinMode(NB_Bus_out, OUTPUT);
pinMode(NB_Bus_start, OUTPUT);
pinMode(LEDpin, OUTPUT);
attachInterrupt(0, NB_get, RISING);

Serial.begin (115200);
Serial.println("Promy Nbus Interface - based on ARDEX v0.3 and emonTX 3 Phase");
Serial.println ("Arduino Hardware Expansion Slave - Nbus - OpenEnergyMonitor.org") ;
analog.begin(4);
if ((analog.Vcc < 3500) || ArdEx5VoltIsOK) // switched to 3.3 volt? (3300 mV)
{
Wire.begin(I2C_ADDR);
Wire.onRequest(I2CrequestEvent);
Wire.onReceive(I2CreceiveEvent);
Serial.print("Vcc = ");
Serial.print(((float)analog.Vcc) / 1000);
Serial.println (" Volts.");
Serial.println ("I2C is ready for rPi.");
digitalWrite (LEDpin, HIGH);
}
else
{
Serial.print("Incoming i2c pin volts = ");
Serial.println ((float)(analog.Vcc * analog.read(4)) / 1023000L);
Serial.print("But Arduino is on ");
Serial.print(((float)analog.Vcc) / 1000);
Serial.println (" Volts. Please switch to 3.3");
while (true) // loops forever
{

digitalWrite (LEDpin, HIGH);
delay(100);
digitalWrite (LEDpin, LOW);
delay(100);
}
}
}

void I2CreceiveEvent(int howMany)
{
int ichr;
k=-1;
String Bericht;
while(Wire.available()>0)
{
ichr = Wire.read();
if (k<0) {
opdrachttype = char(ichr);
}
else {
Bericht = Bericht + char(ichr);
}
k++;
}
Bericht = Bericht.substring(1);
char Bericht2[k];
Bericht.toCharArray((char*)Bericht3,k);
readyflag= 'X';
}

void opdrachtverwerken()
{
switch (opdrachttype)
{
case 'C':
opdrachttype = 'A';
Verbruikweten = 0;
if (k == 7){
I2CResultaat = nbus_out(Bericht3);
Serial.println(I2CResultaat);
readyflag = 'Y';
readyflag2 = 0;
} ;
break;

//other options ==> add later
}
}

void I2CrequestEvent()
{

if ( readyflag == 'Y'){
Wire.write('Y');
}
else {
Wire.write('X');
readyflag2 = 0;
}
++readyflag2;

if (Verbruikweten == 0 && readyflag2 > 1){
Wire.write(I2CResultaat[index]);
Serial.println(I2CResultaat[index]);
++index ;
if (index >= I2CResultaat.length()){
index = 0;
readyflag = 'X';
readyflag2 = 0;
}
}

}

void NB_get()
{ unsigned long puls;
detachInterrupt(0);
knop=0;
do
{ puls = pulseIn(NB_Bus_in, LOW,3600);
}while (!((puls>3000)&&(puls<3200))&&!(puls==0)) ;
if (puls>0) // no error
{ for (int NB_i=1;NB_i<=24;NB_i++)
{ puls = pulseIn(NB_Bus_in, LOW,3600);
knop=knop << 1;
if (puls>=1000&&puls!=0){knop|=0b1;}
}
} else goto einde;
nbus=knop;
einde:
attachInterrupt(0, NB_get, RISING);
}

Second part (because of the 9500 char max)

void nbus_send_byte(int NB_aantal,...)
{ int NB_Byte;
va_list NB_Bytes;
va_start(NB_Bytes, NB_aantal);
detachInterrupt(0);
if (NB_aantal == 3) {nbus_Pulsout(0);}
if (NB_aantal == 3) {nbus_Pulsout(0);}
if (NB_aantal == 3) {nbus_Pulsout(3);}
for (int Nb_k = NB_aantal; Nb_k>0; Nb_k--)
{ NB_Byte=va_arg(NB_Bytes, int);
for (int Nb_k = 7; Nb_k>=0; Nb_k--)
{ nbus_Pulsout(bitRead(NB_Byte,Nb_k));
}
}
if (NB_aantal == 3) {nbus_Pulsout(5);}
if (NB_aantal == 3) {attachInterrupt(0, NB_get, RISING);}
}

String nbus_out(volatile char *tagStg9)
{
byte byte1, byte2, byte3;
int aLen = 3;
byte val1 = 0;
byte val2 = 0;
byte hexVal[3];
char dig1;
char dig2;
int i =0;
int j =0;
for (i= aLen - 1; i >= 0; i--)
{
dig1 = tagStg9[j++];

dig2 = tagStg9[j++];

if(dig1 >= '0' && dig1 <= '9')
val1 = dig1 - '0';
else
val1 = dig1 - 'A' + 10;
if(dig2 >= '0' && dig2 <= '9')
val2 = dig2 - '0';
else
val2 = dig2 - 'A' + 10;
hexVal_ = val1 * 16 + val2; // The ascii value_

  • }*

  • byte1 = byte(hexVal[2]);*

  • byte2 = byte(hexVal[1]);*

  • byte3 = byte(hexVal[0]);*
    *for (int Nb_i = 1; Nb_i<=3; Nb_i++) nbus_send_byte(3,~byte1, ~byte2, ~byte3); *
    return "OK";
    }
    void nbus_Pulsout(int Tijd)
    *{ *

  • digitalWrite(NB_Bus_out, HIGH); *

  • delayMicroseconds(160); *

  • digitalWrite(NB_Bus_out, LOW);*
    _ Tijd=640+(Tijd*800);_

  • delayMicroseconds(Tijd);*

}
long readVcc()
{

  • long result;*
  • ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);*
  • delay(2);*
  • ADCSRA |= _BV(ADSC);*
  • while (bit_is_set(ADCSRA,ADSC));*
  • result = ADCL;*
  • result |= ADCH<<8;*
  • result = 1126400L / result;*
  • return result;*
    }
    void loop ()
    {
  • if (opdrachttype != 'A') {*
  • opdrachtverwerken(); *
  • }*
    _ Serial.println(nbus,HEX);_
  • delay(500);*

}
[/quote]

Conventional wisdom is that interrupt routines should be small and fast. An alternative way to do this would be to have the interrupt be triggered on falling and just set a flag to note that the initial start pulse has been detected. Have the code in loop use this flag to start polling the input to read your data.

BTW, For large sketches, there is the ability to attach your code under "additional options".

String nbus_out(volatile char *tagStg9)  
{
        
return "OK";

}

What a waste of resources. This function might as well have a return type of void, since the return value is ALWAYS the same.

@PaulS: Maybe a waste of recources, but this way i know that it has executed and i can return it to the i2c master.
@wildbill: when working with a flag, isn't there a greater chance of missing the pulses because the loop wasn't there yet?

promy:
@wildbill: when working with a flag, isn't there a greater chance of missing the pulses because the loop wasn't there yet?

It's a potential risk of course, but look at the timing; your signals are measured in (small) numbers of milliseconds - the arduino is plenty fast enough to keep up, unless you throw in delays or serial activity while you're measuring. IIRC, a single instruction on the Uno takes ~65nS - shouldn't be a problem.

I tried it with a flag but no luck...still random reading... and never the correct one.

promy:
I tried it with a flag but no luck...still random reading... and never the correct one.

Then I suggest that you write a new sketch that does nothing but read the signal into an array when the interrupt sets the flag and displays the results via serial when it's done reading. Then post the sketch (code tags, not quotes) and its results. Of course, since you're the one with the hardware, even that may not be enough, but it should help.

but this way i know that it has executed and i can return it to the i2c master.

An int return type, and a return statement that returned 42 would return the same amount of information.