Using WiFI Location on Tracker One

The Tracker One has an onboard ESP32 for WIFI geolocation using google. Will the off the shelf google-maps-device-locator library work directly with the Tracker One?

Support has not been added for Wi-Fi geolocation in the Tracker Edge firmware yet. It is still a planned feature.

Theoretically you could communicate with the ESP32, get the Wi-Fi scan results, and use the Google Geolocation API via the google-map-device-locator. However in practice this is not trivial because the ESP32 is connected by SPI and a pain to communicate with.

Thank you for the fast response. Is there a full schematic posted for the tracker SOM somewhere? Also, what firmware is loaded on the ESP32-D2WD when it comes with the Tracker One. Is there a way to flash custom firmware onto the ESP32?

The Tracker SoM, as is the case with the other production modules (E Series, B Series SoM) is not open source so there’s nothing beyond what is available in the datasheet.

The ESP32 on the Tracker SoM is flashed with the ESP-AT AT command processor. In theory it can be reprogrammed, but you need the full ESP32 toolchain. You might also need a pass-through app on the nRF52 MCU. However the ESP-AT firmware should have everything needed to do Wi-Fi location as-is.

The completed Wi-Fi geolocation feature should be available by January or February 2021.

Thanks for all of the help! I see in the tracker-edge firmware within the tracker-config.h file all of the communication ports are set up. It appears that they are somehow given codes that the compiler understands in order to set things up correctly. I see in the documentation the ESP32 uses SPI1 and WIFI CS for the chip select line. How do you define the interrupt pin such that the compiler knows where to point? It says in the datasheet for the tracker SOM that for the interrupt ESP32 IO4 is connected to MCP23S17 I/0 Expander GPA4.

There are multiple versions of ESP-AT. Can you point me to what is installed so that I can attempt to communicate with the ESP32 chip?

I believe the Tracker SoM ESP32 is using our fork, esp32-at.

It appears that the interrupt pin for the ESP32 chip can be addressed by calling (WIFI_INT).

Can you give an example for sending an AT command via SPI? I have tried many things, and the ESP32 never responds on the MISO line, all I receive are ones.

You’ll also need to set the boot and en pins:

    pinMode(WIFI_CS, OUTPUT);
    digitalWrite(WIFI_CS, HIGH);
    pinMode(WIFI_BOOT, OUTPUT);
    digitalWrite(WIFI_BOOT, HIGH);
    pinMode(WIFI_EN, OUTPUT);
    digitalWrite(WIFI_EN, LOW);
    delay(500);
    digitalWrite(WIFI_EN, HIGH);

Make sure you are using SPI1 you may need to use SPI_MODE0 .

Hi rickkas7,

We are also considering the tracker one as our gateway and need wifi geo location. From above I understand the ESP is flashed with the ESP-AT but as @Ewachlin outlined there is not documentation on how to communicate AT commands via SPI to the ESP32.

Wi-Fi geolocation is planned for testing later this year and release early in 2021. While in theory you could implement it now, it’s undocumented because it will be implemented in the Tracker Edge firmware and implementing it yourself will make your firmware incompatible with future versions of Tracker Edge.

Hi rickkas7,

I studied the ESP32-at code you shared and put together the following code. This replaces the normal main code within the tracker-edge firmware.

Basics of how I see the ESP-AT is looking to communicate is - We tell it in 1 byte whether it should be sending or receiving data. First send a 1 to tell it to receive data The next 4 bytes are used to tell it the length of the data, then the data is sent over the bus. The CS line is cycled between each transaction. Next we tell it to send us data with 1 byte sending a 2. Next we wait for the handshake line to go high saying it is ready to send data and then we expect to receive 4 bytes that contains info about the length of the data to be sent from the ESP32. Finally, we look to receive the data from the ESP32.

I think that is what the following code is doing, but I am not receiving good data. I also noticed that particle’s function for SPI transfer uses DMA. Both need to be in SPI mode 0, but I don’t think that ESP supports DMA on SPI mode 0.

Any help would be greatly appreciated

/*
 * Copyright (c) 2020 Particle Industries, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 * 
 * https://github.com/particle-iot/esp32-at/blob/master/main/interface/hspi/at_hspi_task.c
 */

#include "Particle.h"

#include "tracker_config.h"
#include "tracker.h"
#include "math.h"


double nowtime = 0; // Variable used to loop through transaction only once per second
char RxBuffer[16] = {0}; // Empty buffer for sending data to ESP32
char TxBuffer[16] = {0};  // Buffer to hold actual AT commands to be sent to ESP32
char TxBufferRec[16] = {0}; // Empty buffer used when recieving data from ESP32
char RxBufferRec[16] = {0}; // Buffer to be filled with data recieved from ESP32
uint8_t master[1] = {1}; // Buffer for informing ESP32 that for the next two transactions it will be recieving information
uint8_t slave[1] = {2};  // Buffer for informing ESP32 that for the next two transactions it will be sending data.
uint8_t dummy[1] = {0};  //dummy buffer sent with slave or master
uint8_t Tx_size[4] = {0}; // Buffer for specifying the length of data to be sent to the ESP32
uint8_t Rx_size[4] = {0}; // Dummy buffer for first transaction, filled on second.
uint8_t Rx_dummy[4] = {0}; //dummy buffer for when recieving info on length of data from ESP32
int Inter = 1;  // Interrupt from ESP32
int intCount = 0;  //Interrupt counter.


SYSTEM_THREAD(ENABLED);
SYSTEM_MODE(SEMI_AUTOMATIC);

PRODUCT_ID(TRACKER_PRODUCT_ID);
PRODUCT_VERSION(TRACKER_PRODUCT_VERSION);

SerialLogHandler logHandler(115200, LOG_LEVEL_TRACE, {
    { "app.gps.nmea", LOG_LEVEL_INFO },
    { "app.gps.ubx",  LOG_LEVEL_INFO },
    { "ncp.at", LOG_LEVEL_INFO },
    { "net.ppp.client", LOG_LEVEL_INFO },
});
// Blank function to run after SPI transfer
    void onTransferFinished() {
};

//Settings for SPI transfer to ESP32
__SPISettings settings(6*MHZ, MSBFIRST, SPI_MODE0);


void setup()
{
    Tracker::instance().init();

    Particle.connect();
// Start Serial to see what is happening
    Serial.begin(9600);
//Set up SPI Bus
    SPI1.begin(ESP32_SPI_CS_PIN);
    SPI1.setClockSpeed(6, MHZ);
    SPI1.setDataMode(SPI_MODE0);
    // Fill TX buffer with AT command for testing
    TxBuffer[0] = 'A';
    TxBuffer[1] = 'T';
    TxBuffer[2] = '\r';
    TxBuffer[3] = '\n';
    // Specify length of data buffer and add an A in location 3 as the ESP32-at is looking for that 65 == "A"
    Tx_size[0] = 16;
    Tx_size[3] = 65;

//Set the ESP 32 int pin as an input
    pinMode(ESP32_INT_PIN, INPUT);
//Initialize EPS32
    pinMode(WIFI_CS, OUTPUT);
    digitalWrite(WIFI_CS, HIGH);
    pinMode(WIFI_BOOT, OUTPUT);
    digitalWrite(WIFI_BOOT, HIGH);
    pinMode(WIFI_EN, OUTPUT);
    digitalWrite(WIFI_EN, LOW);
    delay(500);
    digitalWrite(WIFI_EN, HIGH);
    
}

void loop()
{
     Tracker::instance().loop();
     //Timing loop to run through SPI transaction once per second.
     if(millis()-nowtime > 1000){
         nowtime = millis();
         Inter = digitalRead(ESP32_INT_PIN);  // Check state of interrupt pin
         Serial.println(Inter); // print state of interupt pin
         //send data to tell ESP32 it will be recieving data
         SPI1.beginTransaction(settings);
         digitalWrite(ESP32_SPI_CS_PIN, LOW);
         SPI1.transfer(master, dummy, 1, onTransferFinished);
         digitalWrite(ESP32_SPI_CS_PIN, HIGH);
         //Print out master command and return from ESP32.
         Serial.print("The master command is ");
         for (int i = 0; i < sizeof(master); i++){
         Serial.print(master[i]);
         }
         delay(1);
         Serial.print("Return from ESP32 for send command ");
         Serial.println(dummy[1]);

         // Send the size of master data
         digitalWrite(ESP32_SPI_CS_PIN,LOW);
         SPI1.transfer(Tx_size, Rx_size, 4, onTransferFinished);
         digitalWrite(ESP32_SPI_CS_PIN, HIGH);
         delay(1);
         //Print what was recieved from the bus when the Tx size was sent.
         for (int i = 0; i < sizeof(Rx_size); i++){
         Serial.print(Rx_size[i], DEC);
         }
         Serial.println("   End of Rx_size Data");

         // Send the actual AT command
         digitalWrite(ESP32_SPI_CS_PIN, LOW);
         SPI1.transfer(TxBuffer,RxBuffer, 16, onTransferFinished);
         digitalWrite(ESP32_SPI_CS_PIN, HIGH);
         delay(1);

         //Tell the ESP32 it can send data
         digitalWrite(ESP32_SPI_CS_PIN, LOW);
         SPI1.transfer(slave, dummy, 1, onTransferFinished);
         digitalWrite(ESP32_SPI_CS_PIN, HIGH);

         //Wait for interrupt pin from ESP32
         while(Inter < 1 && intCount < 10){
             Inter = digitalRead(ESP32_INT_PIN);
             Serial.print(Inter);  //print state of interrupt pin
             delay(2);
             intCount++;
         }
         Serial.println(Inter); // print final state of interrupt pin
         intCount = 0; // reset interrupt time counter
         //Recieve size of data that will be sent from ESP32
         digitalWrite(ESP32_SPI_CS_PIN, LOW);
         SPI1.transfer(Rx_dummy,Rx_size, 4, onTransferFinished);
         digitalWrite(ESP32_SPI_CS_PIN, HIGH);
        //Check state of interrupt pin and print it
         Inter = digitalRead(ESP32_INT_PIN);
         Serial.print("Inter pre ");
         Serial.println(Inter);
         delay(2);
         // Wait for interrupt pin
         while(Inter < 1 && intCount < 10){
             Inter = digitalRead(ESP32_INT_PIN);
             Serial.print(Inter);
             delay(2);
             intCount++;
         }
         Serial.print("Inter post");
         Serial.println(Inter); // print state of interrupt pin
         intCount = 0;  // Reset interrupt time counter
         // Recieve data from ESP32
         digitalWrite(ESP32_SPI_CS_PIN, LOW);
         SPI1.transfer(TxBufferRec,RxBufferRec, 16, onTransferFinished);
         digitalWrite(ESP32_SPI_CS_PIN, HIGH);
         SPI1.endTransaction(); // close out of the SPI bus
         //Print out the Rx size data
         for (int i = 0; i < sizeof(Rx_size); i++){
         Serial.print(Rx_size[i], DEC);
         Serial.print("  ");
         }
         Serial.println("End of Rx_size");
         //Print out the recieved data
         for (int i = 0; i < sizeof(RxBufferRec); i++){
         Serial.print(RxBufferRec[i], DEC);
         Serial.print("  ");
         RxBufferRec[i] =0;  // Reset the buffer to zeros
         }
         Serial.println("End of Rx Buffer");
         

     }
    }


Sorry also added the following to the tracker_config.h file

//  Defining pin and interface mapping for ESP32

 #define ESP32_SPI_INTERFACE                 (SPI1)
 #define ESP32_SPI_CS_PIN                    (WIFI_CS)
#define ESP32_INT_PIN                       (WIFI_INT)

This is what the Serial monitor looks like.

0
The master command is 1Return from ESP32 for send command 0
255255255255   End of Rx_size Data
11
Inter pre 1
Inter post1
255  255  255  1  End of Rx_size
255  255  255  255  255  255  255  252  7  255  255  255  255  255  255  255  End of Rx Buffer
0
The master command is 1Return from ESP32 for send command 0
31255255255   End of Rx_size Data
11
Inter pre 1
Inter post1
255  255  255  255  End of Rx_size
252  7  255  255  255  255  255  255  255  240  31  255  255  255  255  255  End of Rx Buffer
0
The master command is 1Return from ESP32 for send command 0
255192127255   End of Rx_size Data
11
Inter pre 1
Inter post1
255  255  255  255  End of Rx_size
255  255  240  31  255  255  255  255  255  255  255  192  127  255  255  255  End of Rx Buffer
0
The master command is 1Return from ESP32 for send command 0
2552552551   End of Rx_size Data
11
Inter pre 1
Inter post1
159  255  255  255  End of Rx_size
255  255  255  255  192  127  255  255  255  255  255  255  255  1  255  255  End of Rx Buffer
0
The master command is 1Return from ESP32 for send command 0
255255255255   End of Rx_size Data
11
Inter pre 1
Inter post1
255  192  127  255  End of Rx_size
255  255  255  255  255  255  1  255  255  255  255  255  255  255  252  7  End of Rx Buffer
0
The master command is 1Return from ESP32 for send command 0
255255255255   End of Rx_size Data
11
Inter pre 1
Inter post1
255  255  255  1  End of Rx_size
255  255  255  255  255  255  255  252  7  255  255  255  255  255  255  255  End of Rx Buffer
0
The master command is 1Return from ESP32 for send command 0
31255255255   End of Rx_size Data
11
Inter pre 1
Inter post1
255  255  255  255  End of Rx_size
252  7  255  255  255  255  255  255  255  240  31  255  255  255  255  255  End of Rx Buffer
0
The master command is 1Return from ESP32 for send command 0
255192127255   End of Rx_size Data
11
Inter pre 1
Inter post1
255  255  255  255  End of Rx_size
255  255  240  31  255  255  255  255  255  255  255  192  127  255  255  255  End of Rx Buffer
0
The master command is 1Return from ESP32 for send command 0
2552552551   End of Rx_size Data
11
Inter pre 1
Inter post1
159  255  255  255  End of Rx_size
255  255  255  255  192  127  255  255  255  255  255  255  255  1  255  255  End of Rx Buffer
0
The master command is 1Return from ESP32 for send command 0
255255255255   End of Rx_size Data
11
Inter pre 1
Inter post1
255  192  127  255  End of Rx_size
255  255  255  255  255  255  1  255  255  255  255  255  255  255  252  7  End of Rx Buffer
0
The master command is 1Return from ESP32 for send command 0
255255255255   End of Rx_size Data
11
Inter pre 1
Inter post1
255  255  255  1  End of Rx_size
255  255  255  255  255  255  255  252  7  255  255  255  255  255  255  255  End of Rx Buffer
0
The master command is 1Return from ESP32 for send command 0
31255255255   End of Rx_size Data
11
Inter pre 1
Inter post1
255  255  255  255  End of Rx_size
252  7  255  255  255  255  255  255  255  240  31  255  255  255  255  255  End of Rx Buffer
0
The master command is 1Return from ESP32 for send command 0
255192127255   End of Rx_size Data
11
Inter pre 1
Inter post1
255  255  255  255  End of Rx_size
255  255  240  31  255  255  255  255  255  255  255  192  127  255  255  255  End of Rx Buffer
0
The master command is 1Return from ESP32 for send command 0
2552552551   End of Rx_size Data
11
Inter pre 1
Inter post1
159  255  255  255  End of Rx_size
255  255  255  255  192  127  255  255  255  255  255  255  255  1  255  255  End of Rx Buffer

I created a proof-of-concept library for getting the Wi-Fi scan data and using a modified version of the google-maps-device-locator integration to return coordinates on the Tracker SoM:

Again, it’s best to wait until the official support for Wi-Fi geolocation is available, but it is possible to access the hardware now from user firmware.

5 Likes

Thank you Rick. I was able to get the raw data with some slight modifications to your code. If anyone is interested please just ask. Planning to use a classifier with that data for locating similar to what can be found here.

1 Like

To add on Rick’s comments, we’re trying to integrate more of the low level access with the SPI based ESP32 into the device OS so that we can leverage existing ESP32 drivers

2 Likes