Hifive1 revb WiFi

Are there any examples available using the WiFi of the ESP? The goal is that the HiFive fetches some sensor values, passes them to the ESP and the ESP sends them over WiFi to some service.

I tried to flash a program to the ESP (ACM0 is FE310, ACM1 should be ESP) but the serial connection failed. Which baudrate should I use? I tried the default (9600, 115200, …) but no success.

Anyone knows an answer to this? I’d also like to use the WIFI.

The easiest way seems to be by using its “AT” command set. I found the spec for that here. The ESP32 chip is on one of the FE310’s three SPI buses so I need to find which one, and which is its ‘device select’ pin and some examples of dealing with it.

I note that the link to the Expressif AT information at github given in the “Getting Started Guide” is invalid. There is some info on using SPI with the ESP32 here.

Cool, I’ve came to similar conclusions yesterday, except I only found out SPI is used from the schematic PDF. The guide you link to is useful too.

Like you I’m not sure how to set the SPI GPIO pins into “talk to ESP32” mode. Are you aiming for using C? I’m trying to get this working with rust/embedded-hal crates.

Also the original revB bootloader seems to set some AT commands (at least I get them on the serial terminal) to reset the wifi/bluetooth. I suspect it might be worth reverse engineering that as well since sadly we don’t have the sources to it.

And I do not think it is necessary (or desirable) to flash the ESP32 with new microcode. According to the Getting Started Guide for Rev B, it is flashed at the factory with the AT support code. (See page 8) Make sure you are looking at the Rev B guide, because that is the one that talks about the ESP32 chip.

I am doing this in Mecrisp FORTH. There are three sets of SPI control registers in the FE310: one is for the flash memory and the ESP32 is connect to one of the other two. So need to find out which one. Then after that, when dealing with any SPI device there is a need for a “Chip Select” signal that typically comes from a GPIO pin. We need to look at the schematic to see how this is wired up.

There was a promise of a driver in “Q2 of 2019” but I have not seen it yet.

Made some progress. According to the schematic, “SPI_CS2” is the line select for the ESP32. If I understand things correctly (thanks Disasm btw.) it means that if we want to init SPI to talk to the ESP32 we should use std. GPIO pins for MOSI/MISO and CLK and use GPIO9 (identified as SS2 in revA pinout) as the CS which should “wakeup” the ESP32 on transfers/transactions.

I still need to figure out what to send to it to test this out.

There is hardware support for the SPI channels, including FIFO mechanisms on input and output. I think you would want to use those, not bit-banging the GPIO pins. According to the Memory Map (Chapter 4) the SPI registers are mapped at 0x10024000 and 0x10025000 for SPI1 and SPI2.

Yes this appears to already be taken care of by https://github.com/riscv-rust/e310x-hal/blob/master/src/spi.rs on the rust abstraction side.

I set the pins when creating the struct but hardware SPI is used via the registers if the pins match a known setup (in this case it’s the GPIO9 combo one).

I think all I need now is to know what to send and expect back as a basic test of this.

Ok, I see that now on the schematic. The ESP32 Chip Select is GPIO9. The MOSI/MISO/SCK lines are connected to GPIO 3,4,5 so it is SPI1 (base address 0x10024000) is the one to use.

Looking at the ESP32 AT command manual, it looks like a simple command of “AT” should get a response of “OK”. “AT+GMR” should return the version number. AT commands over a serial port have to end with CRLF but I dont know what the SPI interface expects. On the other hand, in addition to the SPI connect, there is also a UART serial connection between the two chips - perhaps the AT commands go over that, in which case they should be at 115200 bps.

Orrr, we could just look at the Zephyr RTOS support for the HiFive1 Rev B :slight_smile:

Hi, I came across this thread because I too try to find examples on how to communicate with the ESP32 over SPI.

After some trying out I figured that the default cloning of the freedom-e-sdk ends you up in some fixed branch from May, in which only the first SPI for the Rev-b is available! So after moving over to bleeding edge I’m at least able to get SPI 1 using spi = metal_spi_get_device(1);

Then I tried to do a metal_spi_init(spi, 1000000); and send a message like ‘AT\r\n’, but all I get back is the letter ‘C’ during the first communication. Can someone point me in the right direction?

master and v201908-branch should be pretty close to each other at the moment as the 201908 release was just made in the last few days.

You should implement protocol on top of SPI communication. See the example here.

This code runs on ESP32 side and answers to the protocol requests.

1 Like

to communicate with ESP32 SOLO you should

  1. configure spi1 with 79207 Hz (or skip it because bootloader has done it before)

  2. To send to ESP32:
    #define ESP_AT_BUFFER_SIZE (1024)
    int drv_esp32_send(const char* cmd)
    int send_len = strlen(cmd);
    char at_flag_buf[4] = {0x02, 0x00, 0x00, 0x00};
    char len_buf[4] = {0x00, 0x00, 0x00, 'A'};

    /* Send SEND request */
    driver_spi1_transfer(4, at_flag_buf, 0);

    /* Slice the length if it over than 127 */
    if (send_len <= ESP_AT_BUFFER_SIZE) {
    len_buf[0] = send_len & 127;
    len_buf[1] = send_len >> 7;
    len_buf[3] = 'A'; // check info
    } else {
    return -1; // Too large data

    /* Send data length */
    driver_spi1_transfer(4, len_buf, 0);

    /* Send data */
    driver_spi1_transfer(send_len, cmd, 0);
    return 0;

  3. To Receive from ESP32:
    int drv_esp32_recv(char* recvbuf, int* len)
    char at_flag_buf[4] = {0x01, 0x00, 0x00, 0x00};
    char len_buf[4] = {0x00, 0x00, 0x00, 0x00};
    int recv_len = 0;

    /* Send RECV request */
    at_flag_buf[0] = 0x1;
    if(driver_spi1_transfer(4, at_flag_buf, 0) != 0)
    return -1;

    /* Receive data length */
    if(driver_spi1_transfer(4, 0, len_buf) != 0)
    return -1;

    /* Check info */
    if(len_buf[3] == 'B')
    recv_len = (len_buf[1] << 7) + len_buf[0];

    /* Receive data */
    if (recv_len < ESP_AT_BUFFER_SIZE ) {
    if(driver_spi1_transfer(recv_len, 0, recvbuf) != 0)
    return -1;
    recvbuf[recv_len] = 0;
    } else {
    //recv_len over size

    // return length
    if(len) {
    *len = recv_len;
    return 0;

My full source code used for FreeRTOS project

Refer to at_hspi_task.c (https://github.com/espressif/esp-at)

I notice you have 5 ms delays before each operation, rather than watching the handshake GPIO pin. Is this the right way to do it, or is the handshake down in driver_spi1_transfer?

I think this is the simple way to test communication with esp32. I used a saleae - hardware debugger to get signal waves from SPI_MOSI, SPI_MISO, SPI_SCK, SPI_CS2, WF_INT pins. I saw that the times esp32-solo recognize and response through spi are 5ms for request, 5ms for data length and 15ms for data transferring.
I was try using handshake pin (WF_INT pin), however it didn’t work smoothly, in some case the handshake didn’t go to low and kept high forever.

Hi, I also used saleae, and finally got WiFi working after that.
I’m checking the handshake pin and it seems to work fine as long as the CS pin and data is sent correctly to the ESP32. I’m not really sure about timing details though, some parts of the code are probably luck, but it works pretty fine. In any case, I shared my test code here: https://github.com/kjarvel/hifive1revb_wifi
(Including a pre-built hex file in hifive1_revb_wifi.zip).
Connect to the UART, enter SSID and pwd to connect to an AP.
If you try it, please share feedback, and feel free to use/change the code as you like…

I too have been attempting to get some conversation going with the SOLO, with mixed success. Posting here in the hope that someone can tell me what I am doing wrong.

I used to get error messages of the form ERROR CODE: 0x010090000, but now I do not. It appears that I can cause the SOLO to do things, but not get any information back from it.


Command: 'AT+CWJAP="T35.24_guest","welcome guest!"'                                                                                                                                       
> AT+CWJAP="T35.24_guest","welcome guest!"                                                                                                                                                
< WIFI DISCONNECT                                                                                                                                                                         
< WIFI CONNECTED                                                                                                                                                                          
< WIFI GOT IP                                                                                                                                                                             
Command: 'AT+CIPSTART="TCP","tauben.newby.org",80'                                                                                                                                        
> AT+CIPSTART="TCP","tauben.newby.org",80                                                                                                                                                 
< CONNECT                                                                                                                                                                                 
Command: 'AT+PING=""'                                                                                                                                                        
> AT+PING=""                                                                                                                                                                 
< +PING:TIMEOUT                                                                                                                                                                           


Command: 'AT+CIPSTATUS'                                                                                                                                                                   
> AT+CIPSTATUS                                                                                                                                                                            

Code here: https://github.com/johnsnewby/hifive-test

(I used the Rust riscv-quickstart as a template and hacked input into the stdout module. Please don’t judge me for this code, it’s just an attempt to see how things work).