Here's some test code. As the esp32 boots, it spits out messages to serial and I'm trying to ignore those messages in the code, but it doesn't seem to work in 'Release' mode. I think there's some race condition or timing issue. But if you step through it using the debugger, it works fine.
I think if I did this using interrupts, it'd work. But I really hate trying to setup interrupts on this processor and I'm trying my best to avoid them.
main.c
:
#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "esp32thing.h"
int main()
{
init_platform();
enable_esp32();
// ensure the esp32 is booted
// alternatively: monitor uart0 for 'ready\r\n'
usleep(3e6);
enable_uart0();
usleep(3e6);
esp32_consume();
usleep(1e6);
char buffer[1024];
print("Checking if the ESP32 is responding to `AT'... ");
esp32_sendwait("AT", 2);
getline_uart0(buffer, sizeof(buffer));
getline_uart0(buffer, sizeof(buffer));
esp32_assert(buffer, "OK", "did not return 'OK' during AT-OK check");
print("OK\n\r");
print("Setting CWMODE to 1 (station mode)... ");
esp32_sendwait("AT+CWMODE=1", 11);
getline_uart0(buffer, sizeof(buffer));
getline_uart0(buffer, sizeof(buffer));
esp32_assert(buffer, "OK", "did not return 'OK' when setting mode");
print("OK\n\r");
print("Getting APs... ");
esp32_sendwait("AT+CWLAP", 8);
getline_uart0(buffer, sizeof(buffer));
printf("AP -> '%s'\n\r", buffer);
//getline_uart0(buffer, sizeof(buffer));
//esp32_assert(buffer, "OK", "did not return 'OK' getting available APs");
cleanup_platform();
return 0;
}
esp32thing.h
:
#ifndef ESP32THING_H
#define ESP32THING_H
#include <ctype.h>
#include <xuartps.h>
#include <xil_printf.h>
#define SLCR_LOCK *((uint32_t *) 0xF8000004)
#define SLCR_UNLOCK *((uint32_t *) 0xF8000008)
#define UNLOCK_KEY 0xDF0D
#define LOCK_KEY 0x767B
#define MIO_CONF_BASEADDR 0xF8000700
#define MIO_OFFSET_ESP32_BOOT0 0x4 * 25
#define MIO_OFFSET_ESP32_EN 0x4 * 26
#define MIO_ESP32_BOOT0 (MIO_CONF_BASEADDR + MIO_OFFSET_ESP32_BOOT0)
#define MIO_ESP32_EN (MIO_CONF_BASEADDR + MIO_OFFSET_ESP32_EN)
#define ESP32_MASK ((1 << 25) | (1 << 26))
#define GPIO_DATA(n) *(((uint32_t*) 0xE000A040) + n)
#define GPIO_DATA_RO(n) *(((uint32_t*) 0xE000A060) + n)
#define GPIO_DIRM(n) *(((uint32_t*) 0xE000A204) + 16*n)
#define GPIO_OEN(n) *(((uint32_t*) 0xE000A208) + 16*n)
#define UART0_CTRL_ADDR 0xE0000000
#define UART0_MODE_ADDR 0xE0000004
#define UART0_FIFO_STATUS_ADDR 0xE000002C
#define UART0_BAUDGEN_ADDR 0xE0000018
#define UART0_BAUDDIV_ADDR 0xE0000034
#define ESP32_SENDWAIT_MAX_TRIES 100
void enable_esp32();
void enable_uart0();
int getline_uart0(char *, int);
int print_uart0(char *, int);
int isempty_uart0();
int esp32_assert(char *, char *, char *);
int esp32_strcmp(char *bufferUnsure, char *bufferGood);
void esp32_sendwait(char *baseCommand, int length);
void esp32_consume();
#endif
esp32thing.c
:
#include "esp32thing.h"
/**
* Enable the ESP32 module
*/
void enable_esp32() {
print("Enabling the ESP32 module... ");
// unlock system level control
SLCR_UNLOCK = UNLOCK_KEY;
// config MIO pins for BOOT0/EN
// - unbuffered
// -> not actually sure what unbuffered means in this context
// does it mean there's no buffer ``amp''?
// - lvcmos 3.3
*((uint32_t*) MIO_ESP32_BOOT0) = 0x00001600;
*((uint32_t*) MIO_ESP32_EN) = 0x00001600;
// relock SCLR
SLCR_LOCK = LOCK_KEY;
// set GPIO direction and output enable for those two MIO pins
GPIO_DIRM(0) = ESP32_MASK;
GPIO_OEN(0) |= ESP32_MASK;
// assert enable high
GPIO_DATA(0) |= (1 << 26);
print("Done!\n\r");
}
/**
* Enable Uart0 (attached to the esp32)
*/
void enable_uart0() {
print("Enabling Uart0... ");
/*
* could use Xilinx's libs for this bit, or we could
* just use the normal ptr things
*/
// reset rx/tx
*(uint32_t *)UART0_CTRL_ADDR = 0b11;
while((*(uint32_t *)UART0_CTRL_ADDR) == 0b11)
{ /* wait for reset */ }
/**
* mode
* 00 - normal uart mode
* 00 - 1 stop bit
* 1xx - no parity
* 0x - 8 bit chars
* 0 - clk src is uart_ref_clk
*/
*(uint32_t *)UART0_MODE_ADDR = 0b0000100000;
// enable rx/tx
*(uint32_t *)UART0_CTRL_ADDR = 0b010100;
// set baudrate to 115200
*(uint32_t *)UART0_BAUDGEN_ADDR = 0x7C;
*(uint32_t *)UART0_BAUDDIV_ADDR = 6;
print("Done!\n\r");
}
int isempty_uart0() {
int status = *(uint32_t *)UART0_FIFO_STATUS_ADDR;
return status & 0b10;
}
/**
* (Blocking) Reads Uart0 until a newline character
*
* @return Returns the number of bytes received
*/
int getline_uart0(char *buffer, int size) {
int idx = 0;
uint8_t c;
do {
c = XUartPs_RecvByte(UART0_CTRL_ADDR);
buffer[idx++] = c;
} while(c != '\n' && idx < size);
buffer[idx] = '\0';
return idx;
}
/**
* (Blocking) Prints a string to Uart0
*
* @ return Returns the number of bytes transmitted
*/
int print_uart0(char *buffer, int length) {
int i = 0;
char c;
for(; i < length; i++) {
c = buffer[i];
XUartPs_SendByte(UART0_CTRL_ADDR, c);
}
return i;
}
/**
* Crappy strcmp function
*/
int esp32_strcmp(char *bufferUnsure, char *bufferGood) {
int i = 0;
while(bufferGood[i] != '\0') {
if (bufferGood[i] != bufferUnsure[i]) {
return 0;
}
i++;
}
return 1;
}
/**
* Crappy assert function
*/
int esp32_assert(char *bufferUnsure, char *bufferGood, char *failureMessage) {
if (!esp32_strcmp(bufferUnsure, bufferGood)) {
print("ASSERT FAILED: ");
print(failureMessage);
print("\n\r");
while(1) {
/* halt */
}
}
return 0;
}
/**
* Checks the rx fifo and consumes it if it's non-empty
* No idea if this actually works.
*/
void esp32_consume() {
char buffer[1024];
while(!isempty_uart0()) {
/* consume startup messages from the rx fifo */
getline_uart0(buffer, sizeof(buffer));
}
}
/**
* Sends a command and blocks/waits for the echo
*/
void esp32_sendwait(char *baseCommand, int length) {
char buffer[100];
esp32_consume();
print_uart0(baseCommand, length);
print_uart0("\r\n", 2);
int tries = 0;
int nextMessageIsResponse = 0;
while(!nextMessageIsResponse && tries++ < ESP32_SENDWAIT_MAX_TRIES) {
getline_uart0(buffer, sizeof(buffer));
printf(">> '%s'", buffer);
nextMessageIsResponse = esp32_strcmp(buffer, baseCommand);
}
if (!nextMessageIsResponse) {
print("sendwait() failed, max tries exceeded waiting for a correct response!\n\r");
while(1)
{ /* halt */ }
}
}