Bee counter project , how to use the VSPI and HSPI with LMIC

Good morning all,
I am coming to you because for more than a week I have been trying to understand how I could use 2 SPI on my ESP32.
The VSPI is used by my RFM95
VSPI_MISO pin19 ESP32
VSPI_MOSI pin23 esp32
VSPI_SCLK pin18 esp32
VSPI_SS pin5 esp32
All is working perfectly on the lora part but When a want to use the HSPI for shift register nothing work.
HSPI MISO pin12 esp32
HSPI SCLK pin14 esp32.

When I just activate the HSPI part for the Shift register all is whorking perfect so the HPSI is working but when I enable the LMIC lora part I have an Failure:
15:18:13.001 → FAILURE
15:18:13.001 → C:\Users\cd06631\OneDrive-Deere&Co\OneDrive - Deere & Co\Documents\Arduino\libraries\arduino-lmic-master\src\lmic\oslmic.c:53


#include <lmic.h>
#include <hal/hal.h>
#include <Wire.h>
#include <SPI.h>

 

SPIClass SPI1(HSPI);
//SPIClass SPI(VSPI);

const int testLed = 13; //onboard LED
const int LATCH = 16; 



//Pins ESP32 wroom 
const byte powerGates1 = 2;
const byte powerGates2 = 17;


// supply control
//const int transistorPin = 16;

const int numberOfGates = 24; // 24 gates, 48 sensors
const int startGate = 0;  //useful for testing
const int endGate = 24;   //useful for testing
const int debeebounce = 30;
const int outputDelay = 20000;  //prints bee counts every 15 seconds
unsigned long lastOutput = 0;
unsigned long currentTime = 0;


//boolean 0 or 1 sensor readings, 1 bee is present
boolean inSensorReading[numberOfGates];
boolean outSensorReading[numberOfGates];

boolean lastInSensorReading[numberOfGates];
boolean lastOutSensorReading[numberOfGates];

boolean checkStateIn[numberOfGates];
boolean checkStateOut[numberOfGates];

int inCount[numberOfGates];
int outCount[numberOfGates];
  
unsigned long startInReadingTime[numberOfGates];
unsigned long startOutReadingTime[numberOfGates];

unsigned long inSensorTime[numberOfGates];
unsigned long outSensorTime[numberOfGates];
 
unsigned long lastInFinishedTime[numberOfGates];
unsigned long lastOutFinishedTime[numberOfGates];
  
unsigned long inReadingTimeHigh[numberOfGates];
unsigned long outReadingTimeHigh[numberOfGates];

unsigned long lastInTime[numberOfGates];
unsigned long lastOutTime[numberOfGates];

unsigned long lastInReadingTimeHigh[numberOfGates];
unsigned long lastOutReadingTimeHigh[numberOfGates];

int totalTimeTravelGoingOut[numberOfGates];
int totalTimeTravelGoingIn[numberOfGates];

int firstTestInVariable[numberOfGates];


int firstTestOutVariable[numberOfGates];

int totalsorti;
int totalentree;

int inTotal;
int outTotal;


int n = 0;



// For normal use, we require that you edit the sketch to replace FILLMEIN
// with values assigned by the TTN console. However, for regression tests,
// we want to be able to compile these scripts. The regression tests define
// COMPILE_REGRESSION_TEST, and in that case we define FILLMEIN to a non-
// working but innocuous value.

#ifdef COMPILE_REGRESSION_TEST
# define FILLMEIN 0
#else
# warning "You must replace the values marked FILLMEIN with real values from the TTN control panel!"
# define FILLMEIN (#dont edit this, edit the lines that use FILLMEIN)
#endif

// LoRaWAN NwkSKey, network session key
static const PROGMEM u1_t NWKSKEY[16] = {****************************************** };

// LoRaWAN AppSKey, application session key
static const u1_t PROGMEM APPSKEY[16] = { **************************************** };

// LoRaWAN end-device address (DevAddr)
// See http://thethingsnetwork.org/wiki/AddressSpace
// The library converts the address to network byte order as needed.
#ifndef COMPILE_REGRESSION_TEST
static const u4_t DEVADDR = 0x*********;

#else
static const u4_t DEVADDR = 0;
#endif

// These callbacks are only used in over-the-air activation, so they are
// left empty here (we cannot leave them out completely unless
// DISABLE_JOIN is set in arduino-lmic/project_config/lmic_project_config.h,
// otherwise the linker will complain).
void os_getArtEui (u1_t* buf) { }
void os_getDevEui (u1_t* buf) { }
void os_getDevKey (u1_t* buf) { }

// payload to send to TTN gateway
static uint8_t payload[3];
static osjob_t sendjob;

// Schedule TX every this many seconds (might become longer due to duty
// cycle limitations).
const unsigned TX_INTERVAL = 30;

// Pin mapping for Adafruit Feather M0 LoRa
const lmic_pinmap lmic_pins = {
  .nss = 5,
  .rxtx = LMIC_UNUSED_PIN,
  .rst = 13,
           // LBT cal for the Adafruit Feather M0 LoRa, in dB
      .dio = {27, 26, LMIC_UNUSED_PIN},
     //   .dio = {27, LMIC_UNUSED_PIN, LMIC_UNUSED_PIN},
    
};


void onEvent (ev_t ev) {
    Serial.print(os_getTime());
    Serial.print(": ");
    switch(ev) {
        case EV_SCAN_TIMEOUT:
            Serial.println(F("EV_SCAN_TIMEOUT"));
            break;
        case EV_BEACON_FOUND:
            Serial.println(F("EV_BEACON_FOUND"));
            break;
        case EV_BEACON_MISSED:
            Serial.println(F("EV_BEACON_MISSED"));
            break;
        case EV_BEACON_TRACKED:
            Serial.println(F("EV_BEACON_TRACKED"));
            break;
        case EV_JOINING:
            Serial.println(F("EV_JOINING"));
            break;
        case EV_JOINED:
            Serial.println(F("EV_JOINED"));
            break;
        /*
        || This event is defined but not used in the code. No
        || point in wasting codespace on it.
        ||
        || case EV_RFU1:
        ||     Serial.println(F("EV_RFU1"));
        ||     break;
        */
        case EV_JOIN_FAILED:
            Serial.println(F("EV_JOIN_FAILED"));
            break;
        case EV_REJOIN_FAILED:
            Serial.println(F("EV_REJOIN_FAILED"));
            break;
        case EV_TXCOMPLETE:
            Serial.println(F("EV_TXCOMPLETE (includes waiting for RX windows)"));
            if (LMIC.txrxFlags & TXRX_ACK)
              Serial.println(F("Received ack"));
            if (LMIC.dataLen) {
              Serial.println(F("Received "));
              Serial.println(LMIC.dataLen);
              Serial.println(F(" bytes of payload"));
            }
            // Schedule next transmission
//             digitalWrite (transistorPin, LOW);
        //     Deep_Sleep_Now();
           // os_setTimedCallback(&sendjob, os_getTime()+sec2osticks(TX_INTERVAL), do_send);
            break;
        case EV_LOST_TSYNC:
            Serial.println(F("EV_LOST_TSYNC"));
            break;
        case EV_RESET:
            Serial.println(F("EV_RESET"));
            break;
        case EV_RXCOMPLETE:
            // data received in ping slot
            Serial.println(F("EV_RXCOMPLETE"));
            break;
        case EV_LINK_DEAD:
            Serial.println(F("EV_LINK_DEAD"));
            break;
        case EV_LINK_ALIVE:
            Serial.println(F("EV_LINK_ALIVE"));
            break;
        /*
        || This event is defined but not used in the code. No
        || point in wasting codespace on it.
        ||
        || case EV_SCAN_FOUND:
        ||    Serial.println(F("EV_SCAN_FOUND"));
        ||    break;
        */
        case EV_TXSTART:
            Serial.println(F("EV_TXSTART"));
            break;
        default:
            Serial.print(F("Unknown event: "));
            Serial.println((unsigned) ev);
            break;
    }
}

void do_send(osjob_t* j){
    // Check if there is not a current TX/RX job running
    
    if (LMIC.opmode & OP_TXRXPEND) {
        Serial.println(F("OP_TXRXPEND, not sending"));
    } else {

  Serial.print("T, ");
  Serial.print(outTotal);
  Serial.print(", ");
  Serial.println(inTotal);
       

             
int16_t outtotalInt = outTotal * 100; // convert to signed 16 bits integer: 0x0929
int16_t intotalInt = inTotal * 100;


uint8_t buffer[4]; // reserve 2 bytes in memory

// Handle high byte (MSB) first; 0x09 for 23.45
// 0x0929 >> 8 shifts the 0x29 out of memory, leaving 0x0009
// 0x0009 does not fit in a single byte, so only 0x09 is stored in buffer[0]:
buffer[0] = outtotalInt >> 8;
buffer[1] = outtotalInt;
buffer[2] = intotalInt >> 8;
buffer[3] = intotalInt;



// Send on port 1, without asking for confirmation:
LMIC_setTxData2(1, buffer, sizeof(buffer), 0); // 0x0929 for 23.45
        Serial.println(F("Packet queued"));
    }
  
    // Next TX is scheduled after TX_COMPLETE event.
}



void setup ()
{
   

  SPI1.begin();
  SPI1.beginTransaction(SPISettings(3000000, MSBFIRST, SPI_MODE2));



 //pinMode (transistorPin, OUTPUT);
   // digitalWrite (transistorPin, HIGH);
    delay(1000);
    Serial.println(F("Starting"));
  
  delay(4000);
  while (!Serial); 
  Serial.begin (115200);
  Serial.println ("Begin switch test.");
  pinMode (LATCH, OUTPUT);
  digitalWrite (LATCH, HIGH);

  pinMode (powerGates1, OUTPUT);
 digitalWrite(powerGates1, LOW);
 pinMode (powerGates2, OUTPUT);
 digitalWrite(powerGates2, LOW);

  //pinMode(testLed, OUTPUT);


 // LMIC init
   os_init();
    // Reset the MAC state. Session and pending data transfers will be discarded.
  LMIC_reset();

    // Set static session parameters. Instead of dynamically establishing a session
    // by joining the network, precomputed session parameters are be provided.
    // On AVR, these values are stored in flash and only copied to RAM
    // once. Copy them to a temporary buffer here, LMIC_setSession will
    // copy them into a buffer of its own again.
    uint8_t appskey[sizeof(APPSKEY)];
    uint8_t nwkskey[sizeof(NWKSKEY)];
    memcpy_P(appskey, APPSKEY, sizeof(APPSKEY));
    memcpy_P(nwkskey, NWKSKEY, sizeof(NWKSKEY));
    LMIC_setSession (0x13, DEVADDR, nwkskey, appskey);
    
   #if defined(CFG_eu868) // EU channel setup
  int channel = 0;
   int dr = DR_SF7;
   for(int i=0; i<9; i++) { 
     if(i != channel) {
       LMIC_disableChannel(i);
     }
   }
   // Set data rate (SF) and transmit power for uplink
   LMIC_setDrTxpow(dr, 14);    // g-band
#elif defined(CFG_us915) // US channel setup
  // Instead of using selectSubBand, which will cycle through a sub-band of 8
  // channels. We'll configure the device to only use one frequency.
  // First disable all sub-bands
  for (int b = 0; b < 8; ++b) {
    LMIC_disableSubBand(b);
  }
  // Then enable the channel(s) you want to use
  //LMIC_enableChannel(8); // 903.9 MHz
  LMIC_enableChannel(17);
#endif
    // Disable link check validation
    LMIC_setLinkCheckMode(0);

    // TTN uses SF9 for its RX2 window.
    LMIC.dn2Dr = DR_SF9;

    // Set data rate and transmit power for uplink
    LMIC_setDrTxpow(DR_SF7,14);

    // Start job
    do_send(&sendjob);
    
    
}

  


byte switchBank1;
byte oldSwitchBank1; 
byte switchBank2;
byte oldSwitchBank2; 
byte switchBank3;
byte oldSwitchBank3; 
byte switchBank4;
byte oldSwitchBank4; 
byte switchBank5;
byte oldSwitchBank5; 
byte switchBank6;
byte oldSwitchBank6; 
byte switchBank7;
byte oldSwitchBank7; 
byte switchBank8;
byte oldSwitchBank8; 

void loop ()
{
   os_runloop_once(); 
//beeProgram();

}

 void beeProgram() {
  currentTime = millis();  
  
  digitalWrite(powerGates1, HIGH);
  digitalWrite(powerGates2, HIGH);
  delayMicroseconds(75); //first 24 gates only need 15us while gates closer to the end need ~40us-75us
  
  digitalWrite (LATCH, LOW);    // pulse the parallel load latch
  delayMicroseconds(3);
  digitalWrite (LATCH, HIGH);

  delayMicroseconds(3);
  
  digitalWrite(powerGates1, LOW);
  digitalWrite(powerGates2, LOW);

  //Reading 24 bits at 1Mhz should take about 24 microseconds,
  //reading 24 bits at 3Mhz should take about 8us
  //reading 48 bits at 3Mhz should take abotu 16us
  switchBank1 = SPI1.transfer (0); //8
  switchBank2 = SPI1.transfer (0); //16
  switchBank3 = SPI1.transfer (0); //24
  switchBank4 = SPI1.transfer (0); //32
  switchBank5 = SPI1.transfer (0); //40
  switchBank6 = SPI1.transfer (0); //48  
  
 
  
  if(switchBank1 != oldSwitchBank1 || switchBank2 != oldSwitchBank2 || switchBank3 != oldSwitchBank3 || switchBank4 != oldSwitchBank4 || switchBank5 != oldSwitchBank5 || switchBank6 != oldSwitchBank6)
  {
    //convert bytes to gate values
    int gate = 0;
    for(int i = 0; i < 8; i++)
    {
      if((switchBank1 >> i) & 1)
          outSensorReading[gate] = HIGH;
      else outSensorReading[gate] = LOW;
      i++;
      if((switchBank1 >> i) & 1)
          inSensorReading[gate] = HIGH;
      else inSensorReading[gate] = LOW;       
      gate++;  
    }
    for(int i = 0; i < 8; i++)
    {
      if((switchBank2 >> i) & 1)
          outSensorReading[gate] = HIGH;
      else outSensorReading[gate] = LOW;
      i++;
      if((switchBank2 >> i) & 1)
          inSensorReading[gate] = HIGH;
      else inSensorReading[gate] = LOW;      
      gate++;  
    }
    for(int i = 0; i < 8; i++)
    {
      if((switchBank3 >> i) & 1)
          outSensorReading[gate] = HIGH;
      else outSensorReading[gate] = LOW;
      i++;
      if((switchBank3 >> i) & 1)
          inSensorReading[gate] = HIGH;
      else inSensorReading[gate] = LOW;       
      gate++;  
    }
    for(int i = 0; i < 8; i++)
    {
      if((switchBank4 >> i) & 1)
          outSensorReading[gate] = HIGH;
      else outSensorReading[gate] = LOW;
      i++;
      if((switchBank4 >> i) & 1)
          inSensorReading[gate] = HIGH;
      else inSensorReading[gate] = LOW;       
      gate++;  
    }
    for(int i = 0; i < 8; i++)
    {
      if((switchBank5 >> i) & 1)
          outSensorReading[gate] = HIGH;
      else outSensorReading[gate] = LOW;
      i++;
      if((switchBank5 >> i) & 1)
          inSensorReading[gate] = HIGH;
      else inSensorReading[gate] = LOW;       
      gate++;  
    }
    for(int i = 0; i < 8; i++)
    {
      if((switchBank6 >> i) & 1)
          outSensorReading[gate] = HIGH;
      else outSensorReading[gate] = LOW;
      i++;
      if((switchBank6 >> i) & 1)
          inSensorReading[gate] = HIGH;
      else inSensorReading[gate] = LOW;      
      gate++;  
    }  
    oldSwitchBank1 = switchBank1;
    oldSwitchBank2 = switchBank2;
    oldSwitchBank3 = switchBank3;
    oldSwitchBank4 = switchBank4;
    oldSwitchBank5 = switchBank5;
    oldSwitchBank6 = switchBank6;
  }

  for (int i = startGate; i < endGate; i++) 
  { 
    if(inSensorReading[i] == HIGH || outSensorReading[i] == HIGH) 
    {
      digitalWrite(testLed, HIGH);
      break;
    }
    digitalWrite(testLed, LOW);
  }

  
  for (int i = startGate; i < endGate; i++) 
  //for (int i = 8; i < 12; i++)
  { 
    if(inSensorReading[i] != lastInSensorReading[i])  //change of state on IN sensor
    { 
      checkStateIn[i] = 0;
      lastInSensorReading[i] = inSensorReading[i];
      inSensorTime[i] = currentTime;
      //Serial.print(i);
      //Serial.print(", ");
      //Serial.println(inSensorReading[i]);
    } 
    if(outSensorReading[i] != lastOutSensorReading[i])  //change of state on OUT sensor
    { 
      checkStateOut[i] = 0;
      lastOutSensorReading[i] = outSensorReading[i];
      outSensorTime[i] = currentTime;
      //Serial.print(i);
      //Serial.print(", ");
      //Serial.println(outSensorReading[i]);
    }       
    if(currentTime - inSensorTime[i] > debeebounce && checkStateIn[i] == 0)  //debounce IN sensor
    {
      checkStateIn[i] = 1; //passed debounce         
      //Serial.print(i);
      //Serial.print(", IN sensor - high_or_low: ");
      //Serial.println(inSensorReading[i]);
      if(inSensorReading[i] == HIGH) //a bee just entered the sensor
      {
        startInReadingTime[i] = currentTime;
        //Serial.print(i);
        //Serial.print(", I ,");
        //Serial.println(currentTime);
      }
      if(inSensorReading[i] == LOW)  //a bee just exits the sensor; that is, it was HIGH, now it is LOW (empty)
      {  
        lastInFinishedTime[i] = currentTime;            
        inReadingTimeHigh[i] = currentTime - startInReadingTime[i]; //this variable is how long the bee was present for
        Serial.print(i);
        Serial.print(", IT ,");
        Serial.print(inReadingTimeHigh[i]);
        Serial.print(", ");    
        if(outReadingTimeHigh[i] < 650 && inReadingTimeHigh[i] < 650){ //should be less than 650ms
          if(currentTime - lastOutFinishedTime[i] < 200){ //the sensors are pretty cose together so the time it takes to trigger on and then the other should be small.. ~200ms
            inTotal++;
            Serial.print(currentTime);
            Serial.print(",");
            Serial.println(1);
          }else{
            Serial.println(currentTime);
          }
        }else{
          Serial.println(currentTime);
        }
      }           
    }
    if(currentTime - outSensorTime[i] > debeebounce && checkStateOut[i] == 0)  //debounce OUT sensor
    {
      checkStateOut[i] = 1; //passed debounce         
      //Serial.print(i);
      //Serial.print(", IN sensor - high_or_low: ");
      //Serial.println(outSensorReading[i]);
      if(outSensorReading[i] == HIGH) //a bee just entered the sensor
      {
        startOutReadingTime[i] = currentTime;
        //Serial.print(i);
        //Serial.print(", O ,");
        //Serial.println(currentTime);
      }
      if(outSensorReading[i] == LOW)  //a bee just exits the sensor; that is, it was HIGH, now it is LOW (empty)
      {  
        lastOutFinishedTime[i] = currentTime;            
        outReadingTimeHigh[i] = currentTime - startOutReadingTime[i]; //this variable is how long the bee was present for
        Serial.print(i);
        Serial.print(", OT ,");
        Serial.print(outReadingTimeHigh[i]);
        Serial.print(", ");        
        if(outReadingTimeHigh[i] < 600 && inReadingTimeHigh[i] < 600){ //should be less than 600ms
          if(currentTime - lastInFinishedTime[i] < 200){ //the sensors are pretty cose together so this time should be small
            outTotal++;
            Serial.print(currentTime);
            Serial.print(",");
            Serial.println(1);
          }else{
            Serial.println(currentTime);
          }
        }else{
          Serial.println(currentTime);
        }
      }          
    }        
  }    

  delay (15);   // debounce

  if (currentTime - lastOutput > outputDelay) 
    {
    //Serial.println("sending data");
   
    sendData(); 


     
    lastOutput = currentTime; 

   
    
    inTotal = 0;
    outTotal = 0; 
  }
}
      

void sendData() {
  
  Serial.print("T, ");
  Serial.print(outTotal);
  Serial.print(", ");
  Serial.println(inTotal);
  
 
// over wifi or ethernet or serial
}

I don’t Know how to declare the 2 SPI with the LMIC

test

Many thanks for your help :slight_smile:
christophe

Some info here Sharing same SPI with LoRa/LMIC and Epaper

Worth doing a search on there first :+1:

From a fault finding approach I would get your none RFM95 SPI device working on its one first, on its own SPI bus (ie not the one RFM95 is on) and make sure that code is stable, then look to bring that code into the normal working LMIC codebase.

I have to ask, ‘bee counter’?

Many thanks for you answer, Yes I have already looked at this link but I did not find any information that could have helped me …
My code is stable when I used the HSPI, I started with this (without LMIC codebase)

with this system, I can count the number of bees that leave and return to the hive. I already have a system that allows my to controle the weight of hives in lora and I want to integrate everything:

1 Like

I’m comming back to my issue to try to use on one side the bus HSPI for my shift register and other side the RFM95.
On the setup I soon I add the line at the begining I have an error :
SPI1.begin();
SPI1.beginTransaction(SPISettings(3000000, MSBFIRST, SPI_MODE2));

SPIClass SPI1(HSPI); // setup a the begining of the code

If I had the same line after lmic on the setup , the message is send only one time but after nothing is working like the

void setup ()
{
   

  SPI.begin();
  SPI1.begin();

 SPI1.beginTransaction(SPISettings(3000000, MSBFIRST, SPI_MODE2));


 //pinMode (transistorPin, OUTPUT);
   // digitalWrite (transistorPin, HIGH);
    delay(1000);
    Serial.println(F("Starting"));
  
  delay(4000);
  while (!Serial); 
  Serial.begin (115200);
 

  //pinMode(testLed, OUTPUT);



 // LMIC init
   os_init();
    // Reset the MAC state. Session and pending data transfers will be discarded.
  LMIC_reset();

    // Set static session parameters. Instead of dynamically establishing a session
    // by joining the network, precomputed session parameters are be provided.
    // On AVR, these values are stored in flash and only copied to RAM
    // once. Copy them to a temporary buffer here, LMIC_setSession will
    // copy them into a buffer of its own again.
    uint8_t appskey[sizeof(APPSKEY)];
    uint8_t nwkskey[sizeof(NWKSKEY)];
    memcpy_P(appskey, APPSKEY, sizeof(APPSKEY));
    memcpy_P(nwkskey, NWKSKEY, sizeof(NWKSKEY));
    LMIC_setSession (0x13, DEVADDR, nwkskey, appskey);
    
   #if defined(CFG_eu868) // EU channel setup
  int channel = 0;
   int dr = DR_SF7;
   for(int i=0; i<9; i++) { 
     if(i != channel) {
       LMIC_disableChannel(i);
     }
   }
   // Set data rate (SF) and transmit power for uplink
   LMIC_setDrTxpow(dr, 14);    // g-band
#elif defined(CFG_us915) // US channel setup
  // Instead of using selectSubBand, which will cycle through a sub-band of 8
  // channels. We'll configure the device to only use one frequency.
  // First disable all sub-bands
  for (int b = 0; b < 8; ++b) {
    LMIC_disableSubBand(b);
  }
  // Then enable the channel(s) you want to use
  //LMIC_enableChannel(8); // 903.9 MHz
  LMIC_enableChannel(17);
#endif
    // Disable link check validation
    LMIC_setLinkCheckMode(0);

    // TTN uses SF9 for its RX2 window.
    LMIC.dn2Dr = DR_SF9;

    // Set data rate and transmit power for uplink
    LMIC_setDrTxpow(DR_SF7,14);

    // Start job
    do_send(&sendjob);


    
    delay(1000);
  // SPI1.begin();
   // SPI1.beginTransaction(SPISettings(3000000, MSBFIRST, SPI_MODE2));

     Serial.println ("Begin switch test.");
  pinMode (LATCH, OUTPUT);
  digitalWrite (LATCH, HIGH);

  pinMode (powerGates1, OUTPUT);
 digitalWrite(powerGates1, LOW);
 pinMode (powerGates2, OUTPUT);
 digitalWrite(powerGates2, LOW);
}

Does anyone have an idea to properly separate the HSPI and the VSPI with the LMIC library?

I try this but it’s not working…:

Many thanks in advance for your help :slight_smile: