jgromes / RadioLib

Universal wireless communication library for embedded devices
https://jgromes.github.io/RadioLib/
MIT License
1.6k stars 400 forks source link

SX1278 with double interrupts (transmitter and recever in one node/device) #160

Closed Wirux closed 4 years ago

Wirux commented 4 years ago

Hi, I want to have two-ways communication in my LoRa device, but I can't create a working sketch using interrupts Dio0, have someone sketch with these functions? What I want to do is toggle mode between transmit and receive Simple flow chart

  1. Send a byteArray from device A to device B
  2. Change mode to a receiver in A device (wait for B device respond)
  3. After then B device captured byteArray, send a message to A device
  4. device A capture what was sent by B device.
  5. Loop all of this

My sketch

#include <RadioLib.h>

byte byteArrTran[18];
byte byteArrRec[18];

byte deviceAddress = 11;
char selectMode = 'T';

SX1278 lora = new Module(10, 2, 9, 3);

int transmissionState = ERR_NONE;

void setup()
{
    Serial.begin(9600);
    // Set at start 0x00 every byte in arrays
    for (int i = 0; i < 18; i++)
        byteArrTran[i] = 0;
    for (int i = 0; i < 18; i++)
        byteArrRec[i] = 0;

    // initialize SX1278 with default settings
    Serial.print(F("[SX1278] Initializing ... "));

    int state = lora.begin();
    if (state == ERR_NONE)
    {
        Serial.println(F("success!"));
    }
    else
    {
        Serial.print(F("failed, code "));
        Serial.println(state);
        while (true)
            ;
    }

    lora.setDio0Action(setFlag);

    transmissionState = lora.startTransmit(byteArrTran, 18);
}

volatile bool enableInterrupt = true;
volatile bool endFlag = false;

void setFlag(void)
{
    // check if the interrupt is enabled
    Serial.println("setFlag");
    if (!enableInterrupt)
    {

        Serial.println("setFlag !inter");
        return;
    }

    // we sent a packet, set the flag
    endFlag = true;
    Serial.println("endFlag true");
}

void transmit()
{
    Serial.print(F("[SX1278] Sending another packet ... "));
    for (int i = 0; i <= 17; i++)
    {
        Serial.print(byteArrTran[i]);
        Serial.print(" ");
    }
    Serial.println();

    //  int transmissionState = lora.transmit(byteArrTran, 18);

    enableInterrupt = false;

    // reset flag
    //   endFlag = false;

    if (transmissionState == ERR_NONE)
    {
        // packet was successfully sent
        Serial.println(F("transmission finished!"));

        // NOTE: when using interrupt-driven transmit method,
        //       it is not possible to automatically measure
        //       transmission data rate using getDataRate()
    }
    else
    {
        Serial.print(F("failed, code "));
        Serial.println(transmissionState);
    }

    // wait a second before transmitting again
    delay(1000);

    // send another one

    enableInterrupt = true;
}

void receive()
{
    lora.startReceive();
    delay(500);

    enableInterrupt = false;
    endFlag = false;

    int state = lora.readData(byteArrRec, 18);
    if (state == ERR_NONE)
    {
        // packet was successfully received
        Serial.println(F("[SX1278] Received packet!"));
        Serial.print(F("[SX1278] Data:\t\t"));
        for (int i = 0; i < 18; i++)
        {
            Serial.print(byteArrRec[i]);
            Serial.print(" ");
        }
    }
    else if (state == ERR_CRC_MISMATCH)
    {
        // packet was received, but is malformed
        Serial.println(F("[SX1278] CRC error!"));
    }
    else
    {
        // some other error occurred
        Serial.print(F("[SX1278] Failed, code "));
        Serial.println(state);
    }
    enableInterrupt = true;
}
void randomAll()
{
    for (int i = 0; i < 18; i++)
        byteArrTran[i] = random(0, 200);
}

void loop()
{
    switch (selectMode)
    {
    case 'O':
        Serial.println("Init");
        selectMode = 'T';
        break;
    case 'T':
        if (endFlag)
        {
            Serial.println("Transmit");
            randomAll();
            transmit();
            delay(1000);
            enableInterrupt = true;
            selectMode = 'R';
        }
        break;
    case 'R':

        if (endFlag)
        {
            Serial.println("Received");
            receive();
            delay(1000);
            enableInterrupt = true;
            selectMode = 'T';
        }
        break;
    }
    delay(200);
    enableInterrupt = true;
    Serial.println(endFlag);
}

SerialPort

[SX1278] Initializing ... success!
0
setFlag
endFlag true
1
Transmit
[SX1278] Sending another packet ... 7 49 73 58 130 72 144 78 123 109 40 165 92 42 187 103 127 129 
transmission finished!
1
Received
setFlag
endFlag true
[SX1278] Received packet!
[SX1278] Data:      72 101 108 108 111 32 87 111 114 108 100 33 0 0 0 0 0 0 0
0
0
0
...
... 
0

As you can see, the transmit work correct, but after receiving the packet from B device endFlag isn't set as true. Device B right now have an example transmit sketch with interrupts. (all time print 0)

jgromes commented 4 years ago

You're using Serial.print from interrupt service routine setFlag - that's a bad idea, Serial needs interrupts to work, so calling it from interrupt context can and will break things.

Next, as is written in the SX127x_Receive_Interrupt example:

https://github.com/jgromes/RadioLib/blob/93080845d213a17eaa25e48d45cb641b93e672f4/examples/SX127x/SX127x_Receive_Interrupt/SX127x_Receive_Interrupt.ino#L67-L75

When you call lora.readData(), the module will be set to standby mode to read the received data. You have to call lora.startReceive() again after that to re-enable Rx mode.

Wirux commented 4 years ago

Thank you for respond My code now is working fine :) If you have some suggestions on how to improve him I will be grateful

I'm not sure if I correct know how "void setFlag(void)" works This function will be called when:

  1. Transmit ended
  2. After "lora.startReceive();" when new packet is incoming Are there any other cases when this function will be called?

Great work :) Working code:

// include the libraries
#include <RadioLib.h>

// declared byteArrs
byte byteArrTran[18];
byte byteArrRec[18];

// declared device ID and preset mode
byte deviceAddress = 11;
char selectMode = 'T';

// declared object LoRa
SX1278 lora = new Module(10, 2, 9, 3);

// declared variables states of communication
int tranState = ERR_NONE;
int recState = ERR_NONE;

// declared interrupts flags
volatile bool enableInterrupt = true;
volatile bool endFlag = true;

void setup()
{
    Serial.begin(9600);
    // set at start 0x00 every byte in arrays
    for (int i = 0; i < 18; i++)
        byteArrTran[i] = 0;
    for (int i = 0; i < 18; i++)
        byteArrRec[i] = 0;

    // initialize SX1278 with default settings
    Serial.print(F("[SX1278] Initializing ... "));

    // start LoRa
    int state = lora.begin();
    if (state == ERR_NONE)
    {
        Serial.println(F("success!"));
    }
    else
    {
        Serial.print(F("failed, code "));
        Serial.println(state);
        while (true)
            ;
    }

    // set the function that will be called interrupt
    lora.setDio0Action(setFlag);
}

// interrupts function
void setFlag(void)
{
    if (!enableInterrupt)
    {
        return;
    }

    // we sent or get a packet, set the flag
    endFlag = true;
}

void transmit()
{
    // reset flags
    enableInterrupt = false;
    endFlag = false;

    // the print sending byte array
    Serial.print(F("[SX1278] Sending byteArrTran: "));
    for (int i = 0; i < 18; i++)
    {
        Serial.print(byteArrTran[i]);
        Serial.print(" ");
    }
    Serial.println();

    // send printed byte array
    // need to use start Transmit because this function give us interrupt
    tranState = lora.startTransmit(byteArrTran, 18);

    // enable the interrupt
    enableInterrupt = true;

    // check transmission
    if (tranState == ERR_NONE)
    {
        // packet was successfully sent
        Serial.println(F("transmission finished!"));
    }
    else
    {
        Serial.print(F("failed, code "));
        Serial.println(tranState);
    }

    // wait a second before transmitting again
}

void receive()
{
    // reset flags
    enableInterrupt = false;

    // clear byteArr
    for (int i = 0; i < 18; i++)
        byteArrRec[i] = 0;

    // read incomming data
    recState = lora.readData(byteArrRec, 18);
    if (recState == ERR_NONE)
    {
        // packet was successfully received
        Serial.println(F("[SX1278] Received packet!"));
        Serial.print(F("[SX1278] Data:\t\t"));
        for (int i = 0; i < 18; i++)
        {
            Serial.print(byteArrRec[i]);
            Serial.print(" ");
        }
        Serial.println();
    }
    else if (recState == ERR_CRC_MISMATCH)
    {
        // packet was received, but is malformed
        Serial.println(F("[SX1278] CRC error!"));
    }
    else
    {
        // some other error occurred
        Serial.print(F("[SX1278] Failed, code "));
        Serial.println(recState);
    }

    // enable the interrupt
    enableInterrupt = true;
}

// random numbers in byteArr for tests
void randomAll()
{
    for (int i = 0; i < 18; i++)
        byteArrTran[i] = random(0, 200);
}

void loop()
{
    switch (selectMode)
    {
    case 'O':
        Serial.println("Init");
        selectMode = 'T';
        break;
    case 'T':
        if (endFlag)
        {
            Serial.println("Transmit");
            randomAll();
            transmit();
            delay(1000);
            selectMode = 'r'; // go to start listen mode
        }
        break;

    // use this mode to start to listen to incoming data
    case 'r':
        recState = lora.startReceive();
        endFlag = false;  //      change endFlag to false
        delay(500);       //      this flag will be changed for true
        selectMode = 'R'; //      when incoming packet will be detected
        break;

    // we started listen mode, wait for a packet
    case 'R':
        if (endFlag)
        {
            Serial.println("Received");
            receive();
            delay(2000);
            selectMode = 'T';
        }
        break;
    }
}
jgromes commented 4 years ago

Regarding setFlag(): that funcion is an ISR - interrupt service routine. The purpose is to quickly execute some short piece of code when triggered by an interrupt. ISRs aren't called - the processor "jumps" to them when some interrupt is triggered.

In the context of SX1278, when the Arduino sees a rising edge on its external interrupt 0 (pin 2, which is connected to SX1278's DIO0), it will jump to that function, execute it, and then jump back to where it was before the interrupt. Therefore, setFlag() isn't called after startTransmit(), startReceive() or any other function, rather it will be executed whenever there's a rising edge on Arduino pin D2.

Kyck-02 commented 4 years ago

Hello I am starting to work with the SX1272 transceiver with the use of this library, by using the example SX127x_Receive_Interrupt.ino to use the interrupt in DIO0 everything works correctly, but my goal is that the microcontroller is in low power consumption and that interruption will wake up, if I add the lines of code that sleep to the microcontroller and test it, yes it wakes up but I have a very high current consumption, since my idea is to use CR2032 batteries so afterwards I tried to put the SX1272 into sleep mode, but at doing this the DIO0 signal stops appearing as I use an oscilloscope to check the pin. So my question is whether the interrupt used in DIO0 and configured with setDio0Action () only works if the SX1272 is in transmit or receive mode?

jgromes commented 4 years ago

@Kyck-02 This is off-topic for this issue. In short, if you put the SX127x chip to sleep mode, it will stop it from receiving data (or doing anything else for that matter). I suggest checking different sleep modes of your microcontroller, using different power source, or having some RTC to periodically wake up the SX127x radio and have short receive windows.

Kyck-02 commented 4 years ago

I appreciate your answer, I am sorry I used this problem to ask my question.