ESP8266 – Step 4.1 The Ported Code

OK, so I mentioned that I needed to make a spin 2 of the ESP8266 Avatar module in the last article. I've fixed the positioning of the ESP8266 socket, so while that board design is out to the fabricator, I'll finally take some time to discuss the code ported from the Java simulation. Before I get started I want to say that when I first came up with the idea to simulate the Micro-controller code that would control the ESP8266 module I worried it might end up being a big waste of time. However, I can report that in the end doing this saved me so much time that would have been spent downloading and flashing code updates every time I debugged a problem and made a fix. I'll probably do more of this in the future based on the success of this project.

Well, lets go ahead and take a look at the Ported code. HereĀ is the C version of the demux receive function. To see how the code morphed from Java to Micro-controller C, here is a link to the Java code to compare it against.

void ESPRX_demux()
{
  while(BB_hasRemaining(&scanBuffer))
  {
    // Get next character and place in lineBuffer
    uint8_t cVal = BB_getByte(&scanBuffer);
    LINE_append(&line, cVal);
 
    switch (dmState)
    {
    case DM_START:
    // If we are at the start always erase existing and re-append
    LINE_setLength(&line, 0);
    LINE_append(&line, cVal);
    // Look for first clue of what we have received
    // for example A of AT<CR> or AT+ or ATE
    if ((cVal == '\r') || (cVal == '\n'))
    {
      //System.out.println("Ignoring blank line...");
      dmState = DM_START;
    }
    else if (cVal == 'A')
    {
      // Could be A of AT or ALREADY CONNECT
      dmState = DM_ECRE1;
    }
    else if (cVal == '+')
    {
      dmState = DM_REDA;
    }
    else if ((cVal == (char)'O') || (cVal == 'E'))
    { // must be OK, ERROR
      dmState = DM_RESULT;
    }
    else if (cVal == 'r')
    {
      // Could be r of "ready" result or a response
      dmState = DM_RERT;
    }
    else
      dmState = DM_RESP;
    break;
    case DM_RERT:
    if (cVal == 'e')
    {
      // we have seen "re" of "ready" result after reset
      dmState = DM_RESULT;
    }
    else
      dmState = DM_RESP;
    break;
    case DM_ECRE1:
    // Look for T of AT<CR> or AT+ or ATE
    if (cVal == 'T')
    {
      dmState = DM_ECRE2;
    }
    else if (cVal == 'L')
    {
      // Found L of ALREADY CONNECT result
      dmState = DM_RESULT;
    }
    else
    {
      // Can't match, go back to start
      dmState = DM_START;
    }
    break;
    case DM_ECRE2:
    // Look for space in "AT Version:"
    if (cVal == ' ')
    {
      // Have a response to Version command
      dmState = DM_RESP;
    }
    else
    {
      // Must be a command string echo
      dmState = DM_ECHO;
    }
    break;
    case DM_REDA:
    if (cVal == 'I')
    {
      dmState = DM_DATA;
    }
    else
    {
      dmState = DM_RESP;
    }
    break;
    case DM_RESP:
    if(cVal == '\n')
    {
      // We have the Response Buffer Line
      //printf("Found Response: %s", lineBuff);
      // Remove \r\n
      uint8_t size = LINE_getLength(&line) - 2;
      LINE_setLength(&line, size);
      memcpy(responseBuff, lineBuff, size);
      // Add NULL terminator
      responseBuff[size] = 0;
      responseFlag = true;
      dmState = DM_START;
    }
    break;
    case DM_ECHO:
    if(cVal == '\n')
    {
      // We have the Echo Buffer Line
      //printf("Found Echo: %s", lineBuff);
      // Remove \r\n
      LINE_setLength(&line, LINE_getLength(&line) - 2);
      memcpy(echoBuff, lineBuff, LINE_getLength(&line));
      echoFlag = true;
      dmState = DM_START;
    } 
    break;
    case DM_RESULT:
    if(cVal == '\n')
    {
      // We have the Echo Buffer Line
      //printf("Found Result: %s", lineBuff);
      // Remove \r\n
      uint8_t size = LINE_getLength(&line) - 2;
      LINE_setLength(&line, size);
      memcpy(resultBuff, lineBuff, size);
      // Add NULL terminator
      resultBuff[size] = 0;
      resultFlag = true;
      dmState = DM_START;
    } 
    break;
    case DM_DATA:
    if(cVal == ':')
    {
      // String[] dataInfo = line.toString().replace(":", "").split(",");
      // Remove ':' character
      uint8_t size = LINE_getLength(&line) - 1;
      LINE_setLength(&line, size);
      printf("Found Data %s\r\n", lineBuff);
      strtok(lineBuff, ",");
      int id = atoi(strtok(NULL, ","));
      int len = atoi(strtok(NULL, ","));
      printf("Id: %d, Len: %d\r\n", id, len);
      while (BB_hasRemaining(&scanBuffer) < len)
      {
        printf(" Not enough data!!! only %d, bytes\r\n", BB_hasRemaining(&scanBuffer));
        ESPRX_getData();
      }
      for (int i = 0; i < len; i++)
      {
        dataBuff[id][i] = BB_getByte(&scanBuffer);
      }
      dataFlag[id] = true;
      dmState = DM_START;
    }
    break;
    }
  }
}

As you can see you don't have to modify overall logic as long as you create some functions that mimic the Java ByteBuffer functions that are used.

However, some places the logic will need to be modified. A case in point is where Java provides a callback thread of execution that notifies of incoming data. My micro controller code does not have an OS or threads so it must check for arrival of new data while trying to process data that is received. In this case testing for data must be done while processing received data. Lets take a look at the get response functions in Java and C.

The following function is the Java variation and the responseBuff object is a BlockingQueue which supports a poll() function which will not wait for data, if its available it returns the String if not it returns a null. If the wait flag is true the BlockingQueue take() function is called which will return the String or block until a String is available in the Queue.

 String getResponseBuff(boolean wait)
 {
   if (wait == false)
   {
     // If no wait, return string or null if empty
     return responseBuff.poll();
   }
   String response = "";
   try
   {
     response = responseBuff.take();
   } catch (InterruptedException ex)
   {
     System.out.println("getResponseBuff interupted");
   }
   return response;
 }

The following function is the Micro controller C version of the getResponse function. Since this is a single thread of execution with an interrupt that receives the ESP8266 data, it must keep checking for new data while waiting for a response.

uint8_t* ESPRX_getResponseBuff(bool wait)
{
  // Try to process more in coming data
  ESPRX_getData();
  ESPRX_demux();

  // If no echo and not waiting exit
  if(!wait && !responseFlag)
    return NULL;

  // if wait is true, loop until response is ready
  while(!responseFlag)
  {
    ESPRX_getData();
    ESPRX_demux();
  }
  responseFlag = false;
  return responseBuff;
}

So that's a small taste of the Java to Micro-controller C porting, if you have been following along and would like to see any additional code details please drop me a line or leave a comment. Well this essentially wraps up the ESP8266 prototype saga. I am thinking of interfacing this prototype to a Sparkfun weather kit which will allow me to sense more that just voltages, as I'll need to count pulses from the anemometer and the rain gauge. I'll probably start that as a new series so be on the lookout.