Simultaneous action from 2 ATmega328p communicating with nRF24L01+

Go To Last Post
10 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi!

I previously wrote here and since then everything was going great up until now. I’m trying to use two ultrasonic sensors in order to measure distance in between them instead of measuring the distance to a random object. I’ve confirmed that such solution works flawlessly if I connect both sensors to the same trigger signal by cable, but the whole idea for me is to have them working remotely, so this option is only a PoC.

In order to get it working remotely I’m trying to use two controllers with nRF24L01+ each in order to synchronize them. nRF24L01+ setup is based on this site. As seen in code below I was hoping, that interrupts on successful transmission are done at the same time, so I tried to put the trigger function in there. That didn’t work, sadly. If I replace trigger function with a blink of a LEDs they seem to work simultaneously for a human eye.

I estimate that the offset must have been larger than 800us while I need it to be less than 10us. That’s based on the speed of sound and my testing setup.

Can anyone suggest a better solution or idea?

Also, I thought that CE signal from both nRFs will drop simultaneously and two same microcontrollers with same frequency will execute same code in the same time, where am I wrong with my current solution?

Hardware, two of each:

  • ATmega328p, ext. oscillator 16MHz, power supply is filtered properly, fuse bits are set as the should,
  • nRF24L01+,
  • US-015 ultrasonic sensor (very similar to HC-SR04).

 

Code, I admit it's a bit lenghty.

Wireless tranceiver, ultrasonic receiver:

 

#define DELAY 2000

#include <stdio.h>
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include "nRF24L01.h"

#define FOSC 16000000
#define BAUD 9600
#define MYUBRR FOSC/16/BAUD-1
#define OVFLW 15625

uint8_t *data;
static uint8_t s=0;
static long t;

//nRF24L01 TRANSMISSION SETUP====================================
void SPI_MasterInit(void)
{
    PORTB |= (1<<PB2);    //CSN high
    PORTB &= ~(1<<PB1);    //CE low
    /* Enable SPI, Master, set clock rate fck/16*/
    SPCR |= (1<<SPE)|(1<<MSTR)|(1<<SPR0);

}

uint8_t WRByteSPI(uint8_t cData)
{
    // Start transmission
    SPDR = cData;

    PORTD |= (1<<PD7);    //diagnostic Yellow LED ON
    while(!(SPSR & (1<<SPIF))); // Wait for transmission complete
    PORTD &= ~(1<<PD7); //diagnostic Yellow LED OFF

    return SPDR;//return received transmission
}

uint8_t *WriteToNrf(uint8_t ReadWrite, uint8_t reg, uint8_t *val, uint8_t antVal)
{
    //"ReadWrite" (1 (Write) or 0 (Read)), "reg" (the register), "*val" (an array with the package)
    cli();    //disable global interrupt

    if (ReadWrite == W)    //W=Write to NRF and check avoid overwriting W_TX_PAYLOAD
    {
        reg = W_REGISTER + reg;    //Add the "Write" bit to the "reg"
    }

    //Static uint8_t is needed to be able to return an array (* at the beginning)
    static uint8_t ret[32];    //Create an array to be returned

    _delay_us(10);
    PORTB &= ~(1<<PB2); //CSN low - nRF starts to listen
    _delay_us(10);
    WRByteSPI(reg);    //set the NRF to read or write mode of "reg"
    _delay_us(10);

    int i;
    for(i=0; i<antVal; i++)
    {
        if (ReadWrite == R && reg !=W_TX_PAYLOAD)  //check for reading
        {
            ret[i]=WRByteSPI(NOP); //Send a dummy bytes to read out the data
            _delay_us(10);
        }
        else
        {
            WRByteSPI(val[i]);    //Send commands to the NRF one at the time
            _delay_us(10);
        }
    }
    PORTB |= (1<<PB2);    //CSN high - nRF goes back to doing nothing

    sei(); //enable global interrupt

    return ret;    //return the array
}

 void nrf24L01_init(void)
 {
     _delay_ms(100); //allow radio to reach power down if shut down

     uint8_t val[5]; //an array of integers to send to the *WriteToNrf function

     val[0]=0x00;
     WriteToNrf(W, EN_AA, val, 1);    //disable auto-acknowledgements

     val[0]=0x01;
     WriteToNrf(W, EN_RXADDR, val, 1);    //enable data pipe 0

     val[0]=0x03;
     WriteToNrf(W, SETUP_AW, val, 1);    //address width setup, width=2+SETUP_AW, max 5)

     val[0]=0x01;                        //0b0000 0001 -> 2,401GHz
     WriteToNrf(W, RF_CH, val, 1);        //choose frequency, 2,400-2,537GHz 1MHz/step

     val[0]=0x07;                        //0dBm, 1Mbps
     WriteToNrf(W, RF_SETUP, val, 1);    //power and data speed setup

     val[0]=0x6D;
     val[1]=0x78;
     val[2]=0x2C;
     val[3]=0xF9;
     val[4]=0xF4;                        //long, random and secure address
     WriteToNrf(W, RX_ADDR_P0, val, 5);    //give the address to the enabled channel
     WriteToNrf(W, TX_ADDR, val, 5);    //transmitter address

     val[0]=0x01;                        //send 1 byte per package
     WriteToNrf(W, RX_PW_P0, val, 1);

     val[0]=0x1E;     //0b0001 1110 - bit 0="0":transmitter or "1":receiver, bit 1="1"=power up,
                         //bit 4="1"=mask_Max_RT i.e. IRQ-interrupt not triggered if transmission fails
     WriteToNrf(W, CONFIG, val, 1);

     //device neeeds 1.5ms to reach standby mode (CE=low)
     _delay_ms(100);
 }

 void transmit_payload(uint8_t * W_Buff)
 {
     WriteToNrf(R, FLUSH_TX, W_Buff, 0);    //Send 0xE1 to glush the registry from old data! W_Buff[] is only here because an array has to be called with an array
     WriteToNrf(R, W_TX_PAYLOAD, W_Buff, 1);//Send the thata in W_Buff to the NRF

     _delay_ms(10);            //NRF needs a 10ms delay after loading the data
     PORTB |= (1<<PB1);        //CE high=transmit the data
     _delay_us(20);            //delay at least 10us!
     PORTB &= ~(1<<PB1);    //CE low - stop transmitting
     _delay_ms(10);            //long delay again before proceeding
 }

 void reset(void)
  {
      _delay_us(10);
      PORTB &= ~(1<<PB2);    //CSN low
      _delay_us(10);
      WRByteSPI(W_REGISTER + STATUS); //write to status registry
      _delay_us(10);
      WRByteSPI(0x70); //reset all IRQ in STATUS registry
      _delay_us(10);
      PORTB |= (1<<PB2);    //CSN back to high
  }

 void INT0_interrupt_init(void)
 {
    DDRD &= ~(1<<PD2); //External interrupt on INT0, making sure that it's input

    EICRA |= (1<<ISC01);
    EICRA &= ~(1<<ISC00); //INT0 activates on falling edge PD2

    EIMSK |= (1<<INT0);      //Enable INT0 interrupt
 }
//END OF nRF24L01 TRANSMISSION SETUP==============================

//Define functions====================================
void ioinit(void);          // initializes IO
void trig(void);
static int uart_putchar(char c, FILE *stream);
uint8_t uart_getchar(void);

static FILE mystdout = FDEV_SETUP_STREAM(uart_putchar, NULL, _FDEV_SETUP_WRITE);
//===================================================

int main (void)
{
    ioinit();

    //LED test
    PORTD |= (1<<PD7);    //Yellow
    PORTC |= (1<<PC3);    //Red
    PORTB |= (1<<PB0);    //Green
    _delay_ms(300);

    SPI_MasterInit();
    nrf24L01_init();
    INT0_interrupt_init();
    uint8_t  x[1];

    _delay_ms(300);
    PORTC &= ~(1<<PC3);
    PORTB &= ~(1<<PB0);
    PORTD &= ~(1<<PD7);

    printf("Let's go!\n");    //printf test
    x[0]=0b00000001;        //some value has to be transmitted

    while(1){
        transmit_payload(x);
        _delay_ms(DELAY);
    }

    return(0);
}

//IO&PRINTF SETUP ======================================

void ioinit (void)
{
    //IOPort init
    DDRB |= (1<<PB0) | (1<<PB1) | (1<<PB2) | (1<<PB3) | (1<<PB5);
    DDRC |= (1<<PC3);
    DDRD |= (1<<PD1) | (1<<PD6) | (1<<PD7);
    //set SS, MOSI, SCK, PB1 (CE in NRF), PB0 (Green LED), PC3 (Red LED), PD7 (Yellow LED), PD1 (TXD) and PD6 (Trig) as outputs.

    PCMSK2 |= (1<<PCINT21); //Enable PCINT21 pin interrupt
    ASSR   |= (1<<EXCLK);   //use ext oscillator
    TCNT1=0;

    sei();

    //PRINTF setup ====================
    //USART Baud rate: 9600
    UBRR0H = ((MYUBRR)>>(8));
    UBRR0L = MYUBRR;
    UCSR0B = (1<<RXEN0)|(1<<TXEN0);

    stdout = &mystdout; //Required for printf init
}

static int uart_putchar(char c, FILE *stream)
{
    if (c == '\n') uart_putchar('\r', stream);

    loop_until_bit_is_set(UCSR0A, UDRE0);
    UDR0 = c;

    return 0;
}

uint8_t uart_getchar(void)
{
    while( !(UCSR0A & (1<<RXC0)) );
    return(UDR0);
}
//END OF IO&PRINTF SETUP =================================

void trig(void){
    PCICR |= (1<<PCIE2);    //Enable PCIE2 interrupt
    PORTD |= (1<<PD6);        //send a trigger signal to US-015
    _delay_us(10);            //it has to be 10us long
    PORTD &= ~(1<<PD6);
}

ISR(PCINT2_vect)
    {
        if (s==1){
            TCCR1B = 0;    //clk off
            PCICR &= ~(1<<PCIE2);    //Disable PCIE1 interrupt
            t=TCNT1;        //read recorded time
            TCNT1=0;
            t=t*10;
            t=t/58;
            printf("%ldmm\n\n",t);   //print mesured distance
            s=0;
            _delay_ms(300);
        }else {
            TCCR1B |= (1<<CS11);  //normal mode clk/8
            s=1;
        }
    }

ISR(INT0_vect)
{
    cli();    //disable global interrupt

    PORTB &= ~(1<<PB1);  //CE Low - stop sending/listening

    trig();            //this is the function that must be synchronized

    reset();        //reset the NRF for further communication

    data=WriteToNrf(R, R_RX_PAYLOAD, data, 1); //deleting this causes 3 blink bug for unknown reason
    PORTB |= (1<<PB1);    //CE IR_High = 'listens' for data...
    sei();    //enable global interrupt
}

 

Wireless receiver, ultrasonic tranceiver. SPI_MasterInit, WRByteSPI, *WriteToNrf,  nrf24L01_init (mostly), reset and INT0_interrupt_init are duplicates.

In void nrf24L01_init(void) the only difference is 0x1F written to CONFIG register instead of 0x1E in order to make it receiver.

 

#include <avr/io.h>
#include <stdio.h>
#include <util/delay.h>
#include <avr/interrupt.h>

#include "nRF24L01.h"

uint8_t *data;
//nRF24L01 TRANSMISSION SETUP====================================
void SPI_MasterInit(void)
{
    PORTB |= (1<<PB2);    //CSN high
    PORTB &= ~(1<<PB1);    //CE low
    /* Enable SPI, Master, set clock rate fck/16*/
    SPCR |= (1<<SPE)|(1<<MSTR)|(1<<SPR0);
}

uint8_t WRByteSPI(uint8_t cData)
{
    // Start transmission
    SPDR = cData;

    while(!(SPSR & (1<<SPIF))); // Wait for transmission complete

    return SPDR;//return received transmission
}

uint8_t *WriteToNrf(uint8_t ReadWrite, uint8_t reg, uint8_t *val, uint8_t antVal)
{
    //"ReadWrite" (1 (Write) or 0 (Read)), "reg" (the register), "*val" (an array with the package)
    cli();    //disable global interrupt

    if (ReadWrite == W && reg != W_TX_PAYLOAD)    //W=Write to NRF and check avoid overwriting W_TX_PAYLOAD
    {
        reg = W_REGISTER + reg;    //Add the "Write" bit to the "reg"
    }

    //Static uint8_t is needed to be able to return an array (* at the beginning)
    static uint8_t ret[32];    //Create an array to be returned

    _delay_us(10);
    PORTB &= ~(1<<PB2); //CSN low - nRF starts to listen
    _delay_us(10);
    WRByteSPI(reg);    //set the NRF to read or write mode of "reg"
    _delay_us(10);

    int i;
    for(i=0; i<antVal; i++)
    {
        if (ReadWrite == R)  //check for reading
        {
            ret[i]=WRByteSPI(NOP); //Send a dummy bytes to read out the data
            _delay_us(10);
        }
        else
        {
            WRByteSPI(val[i]);    //Send commands to the NRF one at the time
            _delay_us(10);
        }
    }
    PORTB |= (1<<PB2);    //CSN high - nRF goes back to doing nothing

    sei(); //enable global interrupt

    return ret;    //return the array
}

 void nrf24L01_init(void)
 {
     _delay_ms(100); //allow radio to reach power down if shut down

     uint8_t val[5]; //an array of integers to send to the *WriteToNrf function

     val[0]=0x00;
     WriteToNrf(W, EN_AA, val, 1);    //disable auto-acknowledgements

     val[0]=0x01;
     WriteToNrf(W, EN_RXADDR, val, 1);    //enable data pipe 0

     val[0]=0x03;
     WriteToNrf(W, SETUP_AW, val, 1);    //address width setup, width=2+SETUP_AW, max 5)

     val[0]=0x01;                        //0b0000 0001 -> 2,401GHz
     WriteToNrf(W, RF_CH, val, 1);        //choose frequency, 2,400-2,537GHz 1MHz/step

     val[0]=0x07;                        //0dBm, 1Mbps
     WriteToNrf(W, RF_SETUP, val, 1);    //power and data speed setup

     val[0]=0x6D;
     val[1]=0x78;
     val[2]=0x2C;
     val[3]=0xF9;
     val[4]=0xF4;                        //long, random and secure address
     WriteToNrf(W, RX_ADDR_P0, val, 5);    //give the address to the enabled channel
     WriteToNrf(W, TX_ADDR, val, 5);    //transmitter address

     val[0]=0x01;                        //send 1 byte per package
     WriteToNrf(W, RX_PW_P0, val, 1);

     val[0]=0x1F;     //0b0001 1111 - bit 0="0":transmitter or "1":receiver, bit 1="1"=power up,
                         //bit 4="1"=mask_Max_RT i.e. IRQ-interrupt not triggered if transmission fails
     WriteToNrf(W, CONFIG, val, 1);

     //device neeeds 1.5ms to reach standby mode (CE=low)
     _delay_ms(100);
 }

void reset(void)
 {
     _delay_us(10);
     PORTB &= ~(1<<PB2);    //CSN low
     _delay_us(10);
     WRByteSPI(W_REGISTER + STATUS); //write to status registry
     _delay_us(10);
     WRByteSPI(0x70); //reset all IRQ in STATUS registry
     _delay_us(10);
     PORTB |= (1<<PB2);    //CSN back to high
 }

 void INT0_interrupt_init(void)
 {
    DDRD &= ~(1<<PD2); //External interrupt on INT0, making sure that it's input

    EICRA |= (1<<ISC01);
    EICRA &= ~(1<<ISC00); //INT0 activates on falling edge PD2

    EIMSK |= (1<<INT0);      //Enable INT0 interrupt
 }
 //END OF nRF24L01 TRANSMISSION SETUP==============================

 void trig(void){
    PCICR |= (1<<PCIE2);    //Enable PCIE2 interrupt to keep the code at the same lenght
     PORTD |= (1<<PD6);
     _delay_us(10);
     PORTD &= ~(1<<PD6);
    PCICR &= ~(1<<PCIE2);    //Disable unnecessary PCIE2 interrupt
 }

int main(void)
{
    //IOPort init
    DDRB |= (1<<PB0) | (1<<PB1) | (1<<PB2) | (1<<PB3) | (1<<PB5);
    DDRC |= (1<<PC3);
    DDRD |= (1<<PD6) | (1<<PD7);
    //set SS, MOSI, SCK, PB1 (CE in NRF), PB0 (Green LED), PC3 (Red LED) and PD7 (Yellow LED) as outputs.

    //LED TEST
    PORTD |= (1<<PD7);    //Yellow
    PORTC |= (1<<PC3);    //Red
    PORTB |= (1<<PB0);    //Green
    _delay_ms(300);

    SPI_MasterInit();
    nrf24L01_init();
    INT0_interrupt_init();

    _delay_ms(300);
    PORTC &= ~(1<<PC3);
    PORTB &= ~(1<<PB0);
    PORTD &= ~(1<<PD7);
    //LEDs are ok, SPI init completed
    _delay_ms(300);

    sei(); //Enable global interrupt
    reset();        //reset the NRF for further communication
    PORTB |= (1<<PB1);    //CE IR_High = 'listens' for data...

    while (1)        //endless loop to wait for signal
        {            //I know that some kind of sleep would be nice in here
        }
}

ISR(INT0_vect)
{
    cli();    //disable global interrupt

    PORTB &= ~(1<<PB1);  //CE Low - stop sending/listening

    trig();        //this is the function that must be synchronized

    reset();        //reset the NRF for further communication

    PORTD |= (1<<PD7);    //Yellow
    _delay_ms(100);
    PORTD &= ~(1<<PD7); //end blink

    data=WriteToNrf(R, R_RX_PAYLOAD, data, 1); //deleting this causes "3 blink bug" for unknown reason
    PORTB |= (1<<PB1);    //CE IR_High = 'listens' for data...
    sei();    //enable global interrupt
}
This topic has a solution.

"Better" is the enemy of "good".

Last Edited: Tue. Jul 30, 2019 - 08:02 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Please paste your code using the CODE tags! Look for the <> button in the toolbar when you post. You can go back and edit your post and fix it.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
    sei();    //enable global interrupt

}

While it's not dangerous to cli() in an ISR() there's no real point as the AVR disables I in SREG on the way ot the ISR() anyway. But what you must NOT do it sei() in an ISR(). If you do the ISR() could be interrupted by another instance of the same event before the handling of the current one is complete. Each time that happens it will leave a raft of registers stored on the stack. If it happens too many times the stack will "eat" all the RAM. Disaster then ensues.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm still not clear on what it is your trying to do, can you draw me a picture.

Jim

 

Click Link: Get Free Stock: Retire early! PM for strategy

share.robinhood.com/jamesc3274
https://www.onegold.com/join/7134f67c2b814c5ca8144a458eccfd61

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

I see talk of microsecond level synchronisation then you talk about ISR()s and radio links which almost certainly will add far more delay/jitter to anything you are trying to synchronise.

 

If the two stations are to measure things then perhaps all they need are accurate coordinating timestamp sources?

 

For example if each makes regular readings then over time it would probably be possible to track the signals (Kalman perhaps?) therefore from the "trend line" you could probably predict where the signal was (or will shortly be) based on the samples at any given time. Then you can time synchronise that to a reading at the other station.

 

EDIT: OK a picture might help...

 

 

The two sensors are taking readings at different times but in this case the distances they are measuring are changing in linear fashion so if you know that at time 50 Sensor 1 reads 37 then it doesn't matter that you don't actually have a reading for Sensor 2 at exactly time 50. You can predict that it would have been 62.5

 

Of course this very simple example relies on the distances changing in a linear fashion so it's very easy to predict where the signal would have been at a time for which you don't have an exact reading. If the reading were not so linear you may need some kind of "curve fitting" and that's where something like a Kalman filter might come into play.

 

Next you have one micro as "master clock" and the other as "slave". Assuming the AVRs are accurately clocked you just need master to send a "time check" packet over the airwaves every now and again so the slave can adjusted his own clock to be in sync. Then he can later pass back his own readings to the master that can then do this "sample matching" exercise I've outlined.

 

Just an idea anyway. ;-)

 

EDIT: actually if you are sending "time sync" packets from master to slave (only needs to be from time to time if their might be some "drift") then actually the timing of the actual samples could be synchronised. Instead of (as the picture shows, CPU1 sampling at 10, 30, 50, 70 and CPU2 sampling at 20, 40, 60, 80 then they could both trigger samples at the same time safe in the knowledge that their clocks were sync'd. Then you don't even need the trend matching as you have the samples from the exact same time frame anyway.

Last Edited: Thu. Jul 18, 2019 - 01:21 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0


@Kartman Done, sorry. Should it be all in green...?

@ki0bk Ok, I'll try.

Firstly, an understanding of US-015 is required. In normal way of working:

-you send a 10us long signal to his trig pin,

-the sensor measures the distance by sending ultrasound signal and measuring time elapsed before it bounces back from obstacle and come back,

-the sensor sends signal from echo pin, the length of which is in directly proportional to the measurement.

 

What I want to do is to trigger sensors from both stations simultaneously, so that the one in Station A reads foreign signal from Station B as his own, sort of tricking him into measuring the distance between A and B instead of A and any_obstacle_in_front_of_A.

 

The result is printed on PC. In practice after turning on Station A the results of measurement should half (I put a screen behind Station B).

 

My target is to replicate this solution.

 

@clawson

About the sei() in ISR(), should I simply remove them? It appears that I was mislead by that tutorial that I've sent in the first post.

 

About the solution, I'm not sure if you understand what I mean. I don't need to get a reading from both sensors, I need to fool one to treat the other as his own source (as explained above). So I don't really have a way of going around it other than triggering them at the same time.

 

clawson wrote:

ISR()s and radio links which almost certainly will add far more delay/jitter to anything you are trying to synchronise.

 

If it was a constant delay that I could define with high precision, maybe I could just add synchronization delay to Station A controller?

It just occured to me, what if both US-015 were connected to a separate controllers receiving signal from master controller? Sending radio signal over a few centimeters sound silly, but if it worked...

"Better" is the enemy of "good".

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Irrehaare wrote:
I was mislead by that tutorial that I've sent in the first post
yeah this is the problem with the internet. That article certainly looks well thought out and professional and yet it contains such a fundamental error. You can never really know the provenance of anything you ever read on the internet ;-)
Irrehaare wrote:
If it was a constant delay that I could define with high precision, maybe I could just add synchronization delay to Station A controller?
yeah that's kind of what the latter parts of my post above were saying. Assuming you can get both AVrs clocks to run in sync (by A sending regular tiechecks to B to keep it in step) then both could agree "we do the thing when our clocks say 100" and both trigger at exactly that moment. This does not require A to actually send an almost instantaneous trigger to B at that very moment - just to have previously ensured their clocks are in step.

 

HOWEVER there is still a bit of an issue with that - because the very message A sends to B to say "the time now is ..." could itself be subject to delay/jitter. That's a tricky nut to crack !

 

I suppose another way to do it is to ensure they BOTH get very accurate time checks from some common reference - the obvious one that springs to mind is GPS. But that would involve both units having a GPS module too.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What about the echo from station A reflecting off object (??? not shown in pic BTW).

How would you tell which signal is heard?   Can you explain more about what the overall (big picture) objective of this project is?

Are you trying to measure the distance to an object, or just detect if the object is between master/slave.

If the later, wouldn't a photo beam be a better interrupter detection device?

 

Jim

 

Click Link: Get Free Stock: Retire early! PM for strategy

share.robinhood.com/jamesc3274
https://www.onegold.com/join/7134f67c2b814c5ca8144a458eccfd61

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

@clawson so that sei() and cli() in the ISR should be simply deleted? 

If I somehow started it with cable in order to get initial sync, how long would it be before the error goes to far (>10us)?

 

@ki0bk The echo of Station A signal can be ignored, it only forces you to wait a while before sending next signal. I tested it with cable connection and it works that way. You will always hear the first signal (from B) because it has twice less distance to travel and that's the signal I care about.

 

Like I said, big picture is basically reproduction of this project, or actually only the localisation part of it. In a hindsight, putting it right below the picture could be a bad idea.

 

So yes, I have to measure the distance between Stations A and B.

"Better" is the enemy of "good".

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I did it! It's working!

 

First I put the remote communication first, then trigger in station A.

Then I've made a new program for B, measuring the delay between the ISR caused by transmission and the trigger (which I have temporarly connected by cable)

 

The result varied between 9679 and 9681. That's a lot but very, very stable. God bless C and real time systems.

 

So I took original program for station B and put 9679us delay before trigger. And that's how it worked!

Thanks a ton to all of you, especially clawson. While you didn't offer straight up solution, the discussion was enough to make me rethink the problem in the best possible way.

"Better" is the enemy of "good".