MKS WIFI for Makerbase Robin: firmware upgrade and new Web Socket features – 4

Spread the love

I bought a FlyingBear Ghost 5 with an integrated WiFi module, but I discovered no web interface. I don’t know why they can’t add a basic user interface. Then I went to see the web interface of the high-end Makerbase cards and realized that it was better they didn’t develop it.

My solution was to modify the firmware to support the Web Socket and develop the Web interface. This feature is compatible with all Makerbase cards with an MKS WiFi module.

Makerbase MKS wifi firmware upgrade mischianti
Makerbase MKS wifi firmware upgrade mischianti

The result of the BeePrint Web interface is in this screenshot.

MKS WiFi BeePrint interface of my FlyingBear Ghost 5
MKS WiFi BeePrint interface of my FlyingBear Ghost 5

I also decided to explain all the development phases of the project. In this article, I would like to expose the hardware infrastructure of the MKS WiFi card that interfaces with the Makerbase Robin Nano.

Now we will analyze the changes I made to the original MKS WiFi module, with the introduction of Web Socket and some minor features to develop Web interface and debug.

MKS WiFi upgrades

I made some changes to the original code, the old functions remain the same, but I must add a WebSocket server service and some endpoint for additional features.

But these changes use a bit of additional memory, so we lost the OTA update for Filesystem. When we need to upload the Filesystem again, we must downgrade the firmware, upload the Filesystem and upload the firmware again.

WebSocket code

The section that manages the message is this.

bool manageMessage(char* readStr, int readSize){
  //transfer file
  #if 0
  if(strstr((const char *)readStr, "M29") != 0)
  {
    if(!verification_flag)
    {
      break;
    }
    if(transfer_state != TRANSFER_IDLE)
    {
      rcv_end_flag = true;
      net_print((const uint8_t *) "ok\n", strlen((const char *)"ok\n"));
      break;
    }
  }
  #endif

  DEBUG_PRINT(F("transfer_file_flag "));
  DEBUG_PRINTLN(transfer_file_flag);

  DEBUG_PRINT(F("verification_flag "));
  DEBUG_PRINTLN(verification_flag);

  DEBUG_PRINT(F("readStr "));
  DEBUG_PRINTLN(readStr);

  DEBUG_PRINT(F("readSize "));
  DEBUG_PRINTLN(readSize);


  if(transfer_file_flag)
  {

    if(!verification_flag)
    {
      return false;
    }
    if(gFileFifo.left() >= readSize)
    {

      gFileFifo.push((char *)readStr, readSize);
      transfer_frags += readSize;


    }

  }
  else
  {


    if(verification_flag)
    {
      int j = 0;
      char cmd_line[100] = {0};
      String gcodeM3 = "";

    #if 0
      if(transfer_state == TRANSFER_BEGIN)
      {
        if(strstr((const char *)readStr, "M110") != 0)
        {

          file_fragment = 0;
          rcv_end_flag = false;
          transfer_file_flag = true;

          if(package_file_first(filePath) == 0)
          {
            /*transfer_state = TRANSFER_READY;
            digitalWrite(EspReqTransferPin, LOW);*/
          }
          else
          {
            transfer_file_flag = false;
            transfer_state = TRANSFER_IDLE;
          }
          net_print((const uint8_t *) "ok\n", strlen((const char *)"ok\n"));
          break;
        }
      }

    #endif

      init_queue(&cmd_queue);

      cmd_index = 0;
      memset(cmd_fifo, 0, sizeof(cmd_fifo));
      while(j < readSize)
      {
        if((readStr[j] == '\r') || (readStr[j] == '\n'))
        {
          if((cmd_index) > 1)
          {
            cmd_fifo[cmd_index] = '\n';
            cmd_index++;


            push_queue(&cmd_queue, cmd_fifo, cmd_index);
          }
          memset(cmd_fifo, 0, sizeof(cmd_fifo));
          cmd_index = 0;
        }
        else if(readStr[j] == '\0')
          break;
        else
        {
          if(cmd_index >= sizeof(cmd_fifo))
          {
            memset(cmd_fifo, 0, sizeof(cmd_fifo));
            cmd_index = 0;
          }
          cmd_fifo[cmd_index] = readStr[j];
          cmd_index++;
        }

        j++;

        do_transfer();
        yield();

      }
      while(pop_queue(&cmd_queue, cmd_line, sizeof(cmd_line)) >= 0)
      {
      #if 0
        point = strstr((const char *)cmd_line, "M28 ");
        if(point != 0)
        {
          if((strstr((const char *)cmd_line, ".g") || strstr((const char *)cmd_line, ".G")))
          {
            int index = 0;
            char *fileName;

            point += 3;
            while(*(point + index) == ' ')
              index++;

            memcpy((char *)filePath, (const char *)(point + index), readSize - (int)(point + index - (int)(&cmd_line[0])));

            gFileFifo.reset();

            transfer_frags = 0;


            transfer_state = TRANSFER_BEGIN;

            sprintf((char *)dbgStr, "Writing to file:%s\n", (char *)filePath);

            net_print((const uint8_t *)dbgStr, strlen((const char *)dbgStr));
          }

        }
        else
      #endif
        {
          /*transfer gcode*/
          //Serial.write(cmd_line, readNum);
          if((strchr((const char *)cmd_line, 'G') != 0)
            || (strchr((const char *)cmd_line, 'M') != 0)
            || (strchr((const char *)cmd_line, 'T') != 0))
          {
            if(strchr((const char *)cmd_line, '\n') != 0 )
            {
              String gcode((const char *)cmd_line);

            //  sprintf((char *)dbgStr, "read %d: %s\n", readNum, cmd_line);

          //    net_print((const uint8_t *)dbgStr, strlen((char *)dbgStr));

              if(gcode.startsWith("M998") && (M3_TYPE == ROBIN))
              {
                net_print((const uint8_t *) "ok\r\n", strlen((const char *)"ok\r\n"));
              }
              else if(gcode.startsWith("M997"))
              {
                if(gPrinterInf.print_state == PRINTER_IDLE)
                //  net_print((const uint8_t *) "M997 IDLE\r\n", strlen((const char *)"M997 IDLE\r\n"));
                  strcpy((char *)dbgStr, "M997 IDLE\r\n");
                else if(gPrinterInf.print_state == PRINTER_PRINTING)
                //  net_print((const uint8_t *) "M997 PRINTING\r\n", strlen((const char *)"M997 PRINTING\r\n"));
                  strcpy((char *)dbgStr, "M997 PRINTING\r\n");
                else if(gPrinterInf.print_state == PRINTER_PAUSE)
                  //net_print((const uint8_t *) "M997 PAUSE\r\n", strlen((const char *)"M997 PAUSE\r\n"));
                  strcpy((char *)dbgStr, "M997 PAUSE\r\n");
                else
                  strcpy((char *)dbgStr, "M997 NOT CONNECTED\r\n");
              //  net_print((const uint8_t *) "ok\r\n", strlen((const char *)"ok\r\n"));
              }
              else if(gcode.startsWith("M27"))
              {
                memset(dbgStr, 0, sizeof(dbgStr));
                sprintf((char *)dbgStr, "M27 %d\r\n", gPrinterInf.print_file_inf.print_rate);
              //  net_print((const uint8_t *) dbgStr, strlen((const char *)dbgStr));
              //  net_print((const uint8_t *) "ok\r\n", strlen((const char *)"ok\r\n"));
              }
              else if(gcode.startsWith("M992"))
              {
                memset(dbgStr, 0, sizeof(dbgStr));
                sprintf((char *)dbgStr, "M992 %02d:%02d:%02d\r\n",
                  gPrinterInf.print_file_inf.print_hours, gPrinterInf.print_file_inf.print_mins, gPrinterInf.print_file_inf.print_seconds);
              //  net_print((const uint8_t *) dbgStr, strlen((const char *)dbgStr));
              //  net_print((const uint8_t *) "ok\r\n", strlen((const char *)"ok\r\n"));
              }
              else if(gcode.startsWith("M994"))
              {
                memset(dbgStr, 0, sizeof(dbgStr));
                sprintf((char *)dbgStr, "M994 %s;%d\r\n",
                  gPrinterInf.print_file_inf.file_name.c_str(), gPrinterInf.print_file_inf.file_size);
              //  net_print((const uint8_t *) dbgStr, strlen((const char *)dbgStr));
              //  net_print((const uint8_t *) "ok\r\n", strlen((const char *)"ok\r\n"));
              }
              else  if(gcode.startsWith("M115"))
              {
                memset(dbgStr, 0, sizeof(dbgStr));
                if(M3_TYPE == ROBIN)
                  strcpy((char *)dbgStr, "FIRMWARE_NAME:Robin\r\n");
                else if(M3_TYPE == TFT28)
                  strcpy((char *)dbgStr, "FIRMWARE_NAME:TFT28/32\r\n");
                else if(M3_TYPE == TFT24)
                  strcpy((char *)dbgStr, "FIRMWARE_NAME:TFT24\r\n");


              //  net_print((const uint8_t *) dbgStr, strlen((const char *)dbgStr));
              //  net_print((const uint8_t *) "ok\r\n", strlen((const char *)"ok\r\n"));
              }
              /*else if(gcode.startsWith("M105"))
              {
                memset(dbgStr, 0, sizeof(dbgStr));
                sprintf((char *)dbgStr, "T:%d /%d B:%d /%d T0:%d /%d T1:%d /%d @:0 B@:0\r\n",
                  (int)gPrinterInf.curSprayerTemp[0], (int)gPrinterInf.desireSprayerTemp[0], (int)gPrinterInf.curBedTemp, (int)gPrinterInf.desireBedTemp,
                  (int)gPrinterInf.curSprayerTemp[0], (int)gPrinterInf.desireSprayerTemp[0], (int)gPrinterInf.curSprayerTemp[1], (int)gPrinterInf.desireSprayerTemp[1]);

              //  net_print((const uint8_t *) dbgStr, strlen((const char *)dbgStr));
              //  net_print((const uint8_t *) "ok\r\n", strlen((const char *)"ok\r\n"));
              }*/
              else
              {
                DEBUG_PRINT(F("GCODE COMMANDS "));
                DEBUG_PRINTLN(gcode);

                if(gPrinterInf.print_state == PRINTER_IDLE)
                {
                  if(gcode.startsWith("M23") || gcode.startsWith("M24"))
                   {
                    gPrinterInf.print_state = PRINTER_PRINTING;
                    gPrinterInf.print_file_inf.file_name = "";
                    gPrinterInf.print_file_inf.file_size = 0;
                    gPrinterInf.print_file_inf.print_rate = 0;
                    gPrinterInf.print_file_inf.print_hours = 0;
                    gPrinterInf.print_file_inf.print_mins = 0;
                    gPrinterInf.print_file_inf.print_seconds = 0;

                    printFinishFlag = false;
                   }
                }
                gcodeM3.concat(gcode);

              }

            }
          }
        }
        if(strlen((const char *)dbgStr) > 0)
        {
          net_print((const uint8_t *) "ok\r\n", strlen((const char *)"ok\r\n"));

          net_print((const uint8_t *) dbgStr, strlen((const char *)dbgStr));
          memset(dbgStr, 0, sizeof(dbgStr));

        }



        do_transfer();
        yield();



      }

      if(gcodeM3.length() > 2)
      {
        package_gcode(gcodeM3, true);
        //Serial.write(uart_send_package, sizeof(uart_send_package));
        /*transfer_state = TRANSFER_READY;
        digitalWrite(EspReqTransferPin, LOW);*/
        do_transfer();

        socket_busy_stamp = millis();
      }


    }
  }
  return true;

}

I wrote a function that isolated the code to call this function in the previous management zone and the new management of TCP messages.

    {
      if (tcp.hasClient()){
        for(i = 0; i < MAX_SRV_CLIENTS; i++){
          //find free/disconnected spot
        #if 0
          if (!serverClients[i] || !serverClients[i].connected()){
          if(serverClients[i]) serverClients[i].stop();
          serverClients[i] = tcp.available();
          continue;
          }
          #else
          if(serverClients[i].connected())
          {
          serverClients[i].stop();
          }
          #endif
          serverClients[i] = tcp.available();
        }
        if (tcp.hasClient())
        {
          //no free/disconnected spot so reject
          WiFiClient serverClient = tcp.available();
          serverClient.stop();

        }
      }
      memset(dbgStr, 0, sizeof(dbgStr));
      for(i = 0; i < MAX_SRV_CLIENTS; i++)
      {
        if (serverClients[i] && serverClients[i].connected())
        {
          uint32_t readNum = serverClients[i].available();

          if(readNum > FILE_FIFO_SIZE)
          {
          //  net_print((const uint8_t *) "readNum > FILE_FIFO_SIZE\n");
            serverClients[i].flush();
          //  Serial.println("flush");
            continue;
          }


          if(readNum > 0)
          {
            char * point;

              uint8_t readStr[readNum + 1] ;

            uint32_t readSize;

            readSize = serverClients[i].read(readStr, readNum);

            readStr[readSize] = 0;

            if (!manageMessage((char*)readStr, readSize)) break;
          }
        }
      }

and here is the management in the WS listener.

void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
  DynamicJsonDocument docwsT(512);
  DeserializationError error;

    switch(type) {
        case WStype_DISCONNECTED:
      webSocket.sendTXT(num, "{\"connection\": false}");

            DEBUG_PRINT(F(" Disconnected "));
            DEBUG_PRINTLN(num, DEC);

            isConnectedWebSocket = false;
//            isConnectedWebSocketAck = false;
//            DEBUG_PRINTF_AI(F("[%u] Disconnected!\n"), num);
            break;
        case WStype_CONNECTED:
            {
                IPAddress ip = webSocket.remoteIP(num);
//                DEBUG_PRINTF_AI(F("[%u] Connected from %d.%d.%d.%d url: %s\n"), num, ip[0], ip[1], ip[2], ip[3], payload);

                DEBUG_PRINT(num);
                DEBUG_PRINT(F("Connected from: "));
                DEBUG_PRINT(ip.toString());
                DEBUG_PRINT(F(" "));
                DEBUG_PRINTLN((char*)payload);

        // send message to client
//        webSocket.sendTXT(num, "{\"type\":\"connection\", \"connection\": true, \"simpleMessage\": "+String(readSingleMessage?"true":"false")+"}");

        isConnectedWebSocket = true;

        memset(dbgStr, 0, sizeof(dbgStr));
        sprintf((char *)dbgStr, "FWV:%s\r\n", firmwareVersion);
        net_print((const uint8_t*)dbgStr, strlen((const char *)dbgStr));

            }
            break;
        case WStype_TEXT:
          DEBUG_PRINT("NUM -> ");DEBUG_PRINT(num);
          DEBUG_PRINT("payload -> ");DEBUG_PRINTLN((char*)payload);

          manageMessage((char*)payload, length);
//          manageMessage(String((char*)payload));
//          error = deserializeJson(docwsT, (char*)payload);
//
//          if (error) {
//            // if the file didn't open, print an error:
//            DEBUG_PRINT(F("Error parsing JSON "));
//            webSocket.broadcastTXT("Error on WS");
//          }else{
//            JsonObject postObjws = docwsT.as<JsonObject>();
//
//            bool startReceiveDevMsg = postObjws[F("startReceiveDevMsg")];
//            if (startReceiveDevMsg==true){
////          readSingleMessage = postObjws[F("singleMessage")];
//          isConnectedWebSocketAck  = true;
//          DEBUG_PRINT(F("Start listening messages SM -> "));
//          DEBUG_PRINTLN(isConnectedWebSocketAck);
//
//              webSocket.broadcastTXT("{\"type\": \"device_msg\", \"receiving\": true}");
//            }else if (startReceiveDevMsg==false){
//          isConnectedWebSocketAck  = false;
//          DEBUG_PRINT(F("Start listening messages SM -> "));
//          DEBUG_PRINTLN(isConnectedWebSocketAck);
//
//          webSocket.broadcastTXT("{\"type\": \"device_msg\", \"receiving\": false}");
//            }
//
//        bool singleMessage = postObjws[F("singleMessage")];
//        DEBUG_PRINT(F("Single message -> "));
//        DEBUG_PRINTLN(singleMessage);
//            if (singleMessage){
//          readSingleMessage = singleMessage;
//            }
//          }

//          DEBUG_PRINTF_AI(F("[%u] get Text: %s\n"), num, payload);

            // send message to client
            // webSocket.sendTXT(num, "message here");

            // send data to all connected clients
            // webSocket.broadcastTXT("message here");
            break;
        case WStype_BIN:
////          DEBUG_PRINTF_AI(F("[%u] get binary length: %u\n"), num, length);
//            hexdump(payload, length);
//
//            // send message to client
//            // webSocket.sendBIN(num, payload, length);
//            break;
        case WStype_ERROR:
        case WStype_FRAGMENT_TEXT_START:
        case WStype_FRAGMENT_BIN_START:
        case WStype_FRAGMENT:
        case WStype_FRAGMENT_FIN:
        case WStype_PING:
        case WStype_PONG:

//          DEBUG_PRINTF_AI(F("[%u] get binary length: %u\n"), num, length);
          DEBUG_PRINT(F("WS : "))
          DEBUG_PRINT(type)
          DEBUG_PRINT(F(" - "))
      DEBUG_PRINTLN((char*)payload);

            // send message to client
            // webSocket.sendBIN(num, payload, length);
            break;
    }

}

Test WebSocket

You can connect with the simple WebSocket client that you can find in this article.

WebSocket client connected to MKS WiFi
WebSocket client connected to MKS WiFi

When the connection starts, you receive the temperature and WiFi information. You can try to add a command like M20 1:, pay attention to the return at the end (use shift + enter), and you receive a set of messages like

REST end points

By default, only one endpoint is exposed, the file upload.

To develop a Web Interface first, I will enable CORS requests. Refer to this tutorial “How to create a REST server on esp8266 or esp32: CORS request, OPTION, and POST“.

Where sendCrossOriginHeader is managed by ENABLE_CORS define.

void setCrossOrigin(){
#ifdef ENABLE_CORS
    server.sendHeader(F("Access-Control-Allow-Origin"), F("*"));
    server.sendHeader(F("Access-Control-Max-Age"), F("600"));
    server.sendHeader(F("Access-Control-Allow-Methods"), F("PUT,POST,GET,OPTIONS"));
    server.sendHeader(F("Access-Control-Allow-Headers"), F("*"));
#endif
};

void sendCrossOriginHeader(){
    DEBUG_PRINTLN(F("sendCORSHeader"));
    server.sendHeader(F("access-control-allow-credentials"), F("false"));
    setCrossOrigin();
    server.send(204);
}

As you can see, It isn’t a multipart form data, but It wants a stream of the file in raw format.

void handleUpload()
{
  DEBUG_PRINTLN(F("Upload request!"));

  setCrossOrigin();

  uint32_t now;
  uint8_t readBuf[1024];

  uint32_t postLength = server.getPostLength();
  String uri = server.uri();


  if(uri != NULL)
  {
    if((transfer_file_flag) || (transfer_state != TRANSFER_IDLE) || (gPrinterInf.print_state != PRINTER_IDLE))
  {
    server.send(409, FPSTR(STR_MIME_TEXT_PLAIN), FPSTR("409 Conflict"));
    return;
  }

    if(server.hasArg((const char *) "X-Filename"))
    {
      if((transfer_file_flag) || (transfer_state != TRANSFER_IDLE))
      {
        server.send(500, FPSTR(STR_MIME_APPLICATION_JSON), FPSTR(STR_JSON_ERR_500_IS_BUSY));
      return;
      }

    file_fragment = 0;
    rcv_end_flag = false;
    transfer_file_flag = true;
    gFileFifo.reset();
    upload_error = false;
    upload_success = false;

    String FileName = server.arg((const char *) "X-Filename");
    //package_gcode(FileName, true);
    //transfer_state = TRANSFER_READY;
    //digitalWrite(EspReqTransferPin, LOW);
    //String fileNameAfterDecode = urlDecode(FileName);
    //package_gcode(fileNameAfterDecode, true);
    //transfer_state = TRANSFER_READY;
    //digitalWrite(EspReqTransferPin, LOW);
    if(package_file_first((char *)FileName.c_str(), (int)postLength) == 0)
    {
      /*transfer_state = TRANSFER_READY;
      digitalWrite(EspReqTransferPin, LOW);*/
    }
    else
    {
      transfer_file_flag = false;
    }
    /*wait m3 reply for first frame*/
    int wait_tick = 0;
    while(1)
    {
      do_transfer();

      delay(100);

      wait_tick++;

      if(wait_tick > 20) // 2s
      {
        if(digitalRead(McuTfrReadyPin) == HIGH) // STM32 READY SIGNAL
        {
          upload_error = true;
        //  Serial.println("upload_error");
        }
        else
        {
      //    Serial.println("upload_sucess");
        }
        break;
      }

      int len_get = get_printer_reply();
      if(len_get > 0)
      {
        esp_data_parser((char *)uart_rcv_package, len_get);

        uart_rcv_index = 0;
      }

      if(upload_error)
      {
        break;
      }
    }

    if(!upload_error)
    {

       now = millis();
      do
      {
        do_transfer();

        int len = get_printer_reply();

        if(len > 0)
        {
        //  Serial.println("rcv");
          esp_data_parser((char *)uart_rcv_package, len);

          uart_rcv_index = 0;
        }

        if(upload_error || upload_success)
        {
          break;
        }

        if (postLength != 0)
        {
          uint32_t len = gFileFifo.left();



          if (len > postLength)
          {
             len = postLength;
          }
          if(len > sizeof(readBuf))
          {
             len = sizeof(readBuf);
          }
          if(len > 0)
          {

            size_t len2 = server.readPostdata(server.client(), readBuf, len);

            if (len2 > 0)
            {
              postLength -= len2;

              gFileFifo.push((char *)readBuf, len2);


              now = millis();
            }
          }

        }
        else
        {
          rcv_end_flag = true;
          break;
        }
        yield();


      }while (millis() - now < 10000);
    }

    if(upload_success || rcv_end_flag )
    {
      server.send(200, FPSTR(STR_MIME_APPLICATION_JSON), FPSTR(STR_JSON_ERR_0));
    }
    //if((millis() - now >= 10000) || upload_error)
    else
    {

      //Serial.print("len:");
      //Serial.println(gFileFifo.left() );

      //Serial.print("postLength:");
      //Serial.println(postLength );
      if(Serial.baudRate() != 115200)
      {
        Serial.flush();
        Serial.begin(115200);
        // Serial.begin(4500000);
      }

      //Serial.println("timeout" );


      transfer_file_flag = false;
      rcv_end_flag = false;
      transfer_state = TRANSFER_IDLE;
      server.send(500, FPSTR(STR_MIME_APPLICATION_JSON), FPSTR(STR_JSON_ERR_500_NO_DATA_RECEIVED));
    }

    }
  else
  {

    server.send(500, FPSTR(STR_MIME_APPLICATION_JSON), FPSTR(STR_JSON_ERR_500_NO_FILENAME_PROVIDED));
    return;
  }
  }
}

I also add a new endpoint in GET and POST to manage the configuration of BeePrint:

  server.on("/configApp", HTTP_OPTIONS, sendCrossOriginHeader);
  server.on("/configApp", HTTP_POST, postConfigFile, NULL);
  server.on("/configApp", HTTP_GET, getConfigFile, NULL);

It stores a file on SPIFFS that can be used to preserve the configuration of BeeTree UI.

void postConfigFile() {
  DEBUG_PRINTLN(F("postConfigFile"));

  setCrossOrigin();


  uint32_t postLength = server.getPostLength();

  if(postLength > maxPostSize || (postLength <= 0))
  {
    server.send(400, FPSTR(STR_MIME_TEXT_PLAIN), FPSTR("Bad Request, wrong post size"));
    return;
  }



  uint8_t readBuf[postLength];
  DynamicJsonDocument doc(postLength);

  if((postLength > sizeof(readBuf)) || (postLength <= 0))
  {
    server.send(400, FPSTR(STR_MIME_TEXT_PLAIN), FPSTR("Bad Request"));
    return;
  }

  server.readPostdata(server.client(), readBuf, postLength);
//    serializeJson(doc, buf);
//    JsonObject& root = jsonBuffer.parseObject((char *)readBuf);
//
//    root = jsonBuffer.parseObject((char *)readBuf);

  DeserializationError error = deserializeJson(doc, readBuf);


    String postBody = server.arg("plain");
    DEBUG_PRINTLN(postBody);

//  DynamicJsonDocument doc(CONFIG_FILE_HEAP);
//  DeserializationError error = deserializeJson(doc, postBody);
  if (error) {
    // if the file didn't open, print an error:
    DEBUG_PRINT(F("Error parsing JSON "));
    DEBUG_PRINTLN(error.c_str());

    String msg = error.c_str();

    server.send(400, F("text/html"), "Error in parsin json body! <br>"+msg);

  }else{
    JsonObject postObj = doc.as<JsonObject>();

    DEBUG_PRINT(F("HTTP Method: "));
    DEBUG_PRINTLN(server.method());

        if (server.method() == HTTP_POST) {
            if (( postObj.containsKey("server") ||
                postObj.containsKey("preferences") ||
          postObj.containsKey("serverSMTP") ||
          postObj.containsKey("camera")
          )) {

              DEBUG_PRINT(F("Open config file..."));
              File configFile = SPIFFS.open(F("/mc/config.txt"), "w");
              if (!configFile) {
                  DEBUG_PRINTLN(F("fail."));
                  server.send(304, F("text/html"), F("Fail to store data, can't open file!"));
              }else{
                DEBUG_PRINTLN(F("done."));
                serializeJson(doc, configFile);
//                httpRestServer.sendHeader("Content-Length", String(postBody.length()));
                server.send(201, F("application/json"), postBody);

//                DEBUG_PRINTLN(F("Sent reset page"));
//            delay(5000);
//            ESP.restart();
//            delay(2000);
              }
            }
            else {
              server.send(204, F("text/html"), F("No data found, or incorrect!"));
            }
        }
    }
}

Test Upload

The upload process can be managed by curl command like this:

curl -i -X POST "http://192.168.1.164/upload?X-Filename=prova.stl" -H "Content-Type: text/xml" --data-binary "@FBG5_prova.gcode"

And here is the response:

*   Trying 192.168.1.164...
* TCP_NODELAY set
* Connected to 192.168.1.164 (192.168.1.164) port 80 (#0)
> POST /upload?X-Filename=prova.stl×tamp=1635695248382 HTTP/1.1
> Host: 192.168.1.164
> User-Agent: curl/7.55.1
> Accept: */*
> Content-Type: text/xml
> Content-Length: 299092
> Expect: 100-continue
>
* Done waiting for 100-continue
* We are completely uploaded and fine
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Cache-Control: no-cache, no-store, must-revalidate
< Pragma: no-cache
< Expires: 0
< Content-Type: application/json
< Content-Length: 9
< Connection: close
<
{"err":0}* Closing connection 0

Compile mischianti version of the MKS WiFi firmware

Now we must compile the Mischianti MKS WiFi version of the firmware. As described in the previous article, you can upload it directly to the MKS WiFi board or NodeMCU device.

MKS WiFi esp12 programming module connection on BreadBoard
MKS WiFi esp12 programming module connection on BreadBoard

First, you must use the 2.7.4 version of esp8266 core because I continue to use the SPIFFS filesystem like the original version.

Then you must set this value to your Arduino IDE:

  • BoardNodeMCU 1.0 (ESP-12E Module)
  • Builtin Led2
  • Upload Speed921600 (115200 is more secure)
  • CPU Frequency: “160MHz” (80MHZ is more secure)
  • Flash Size4MB
  • SPIFFS: 3Mb

And my last build used this set of library

  • ArduinoJson@6.18.4
  • ESP8266WiFi@1.0
  • ESP8266mDNS@1.2
  • EEPROM@1.0
  • ESP8266HTTPClient@1.2
  • WebSockets@2.3.5
Test env for MKS WiFi with NodeMCU esp8266 on Robin Nano
Test env for MKS WiFi with NodeMCU esp8266 on Robin Nano

Activate CORS and DEBUG

To simplify the development of the firmware, I add two define:

#define ENABLE_CORS

that allows you to use an external web interface (not installed on the device), you can get more information by reading the REST tutorial.

#define MKSWIFI_DEBUG

That enable some debug information on a specified Serial

// Define where debug output will be printed.
#define DEBUG_PRINTER Serial1

with this configuration, you can read debug messages on the D4 pin, and you can get more information on this article “WeMos D1 mini (esp8266), debug on secondary UART“.

Generate FileSystem binary file

To get fully working, you must also generate a binary file of Filesystem.

Add verbose output to Arduino IDE

To better understand all processes, we will enable verbose output on our Arduino IDE. You can find these options on File -> Preferences and check Show verbose output checks.

Arduino IDE enable show verbose ouput
Arduino IDE enables show verbose output.

Now we can grab and reuse console commands.

Prerequisite

For the Filesystem, we can do the same, but first, you need to read the following guides WeMos D1 mini (esp8266), integrated SPIFFS Filesystem.

We will add the data directory to the sketch folder (with some files) and use the plugin to upload.

ESP8266 Sketch Data Upload Menu

In the console, we find the correct command to generate this file.

We can now see a lot of information, including the file’s location and the address.

Using Web Interface or SD to upload

Now with the Web Interface, you can follow these steps to upload

  • Take the MksWifi_WebView.bin and upload the Web View;
  • Take MksWifi.bin and upload the firmware.

It’s essential to follow these steps because my firmware version needs more resources, and if you upload firmware first, you can’t upload the Filesystem.

MKS WiFi firmware update page
MKS WiFi firmware update page

So in the future, if you want to upload a new version of Filesystem, you must upload the older version of the firmware and repeat the process.

You can download pre-compiled firmware of the Mischianti version:

  • MksWifi.bin here;
  • MksWifi_WebView.bin here.

You can download pre-compiled original firmware from here:

  • MksWifi.bin here;
  • If you have firmware like Flying Bear Ghost 5, you can put these files inside the SD, insert It in the printer, restart It, wait until the first process is complete, then continue again to upload the remaining file.

    Thanks

    1. MKS WIFI for Makerbase Robin: boards and how to wiring esp12 & NodeMCU
    2. MKS WIFI for Makerbase Robin: PCB and how to compile & upload firmware
    3. MKS WIFI for Makerbase Robin: communication protocol and Cura plugin
    4. MKS WIFI for Makerbase Robin: firmware upgrade and new Web Socket features
    5. MKS WIFI for Makerbase Robin: BeePrint web interface with Camera on Flying Bear Ghost
    All the code is released under Creative Commons License
    Spread the love

    26 Responses

    1. Lazar says:

      upload blocked at 40%, page white….very interesting plugins and looks nice…tnx

    2. zig says:

      i did this but when i try to go to 1.1.1.1/#/home for example i get a 404 eror but the ‘update works fine any help wpild be awesome

    3. raini says:

      Hello great work I uploaded it on my MKS DLC 2.1 and it works! Maybe you can share it on github then you’ll have more support. So far this is the best I’ve seen on the web MKS technically. Are you also working on a CNC version because I have this version.

      Greetings and keep it up!

      • Hi Raini,
        It’s wonderful.
        I released the firmware on GitHub, but I’d like to find some collaborators for the Web interface first, then I’ll open It on GitHub.
        The Web project Is not so simple, and giving support and checking the development needs a lot of time.
        Bye Renzo

    4. Vitaly says:

      Excellent work!
      There are a couple of wishes: The ability to change some parameters (temperature, blowing) after the start of printing. Or at least send a G-code.
      Also incorrect Russian characters on the screen (I can’t add a photo here.)

    5. Nayef Mohammed says:

      Something is wrong the DynamicJsonDocument is not a valid type.

    6. Jon says:

      Hi I uploaded everything according to the video and using the prepared .bin files but I cannot connect to the printer. The homepage appears and everything looks okay but it won’t connect. Any ideas or workarounds? Board: MKS Robin E3P with the ESP32 Wifi module

    7. camarade35 says:

      Bonjour @Renzo et d’abord merci.
      J’ai réussi à ajouter une caméra (non ESP32) mais je ne peux pas redimensionner la frame dédiée, aurais-tu une idée du pourquoi s’il te plait

    8. Andre Kuhlow says:

      Hello,
      I have a Flying Bear Ghost 5 and I installed your extension.
      After it worked for 3 days, it suddenly stopped working.
      I tried again simply to reinstall the two files, but the error persists

      The web interface works and I can home the printer.
      When I select file and upload it, the website turns gray and nothing else happens. The printer display then shows an error.
      The file hasn’t been copied to the SD card either.

      If I copy the file on the computer to the SD card and use the SD card on the printer, I cannot start the printer via the browser. But directly on the display.

      I also tried flashing the original firmware from Flying Bear, but the web interface remains Beeprint’s.

      Help. Can you help? How can I go back to the original firmware if necessary and try again.

      Thank you Andre
      (Translated with google translate)

    9. Tomas C. says:

      Hi I uploaded webUI and than Firmware. If I go to 192.168.3.53/update it looks as in video.
      When I try to go to 192.168.3.53/#/home or any other address I get 404 message like this: {“err”: “404: / NOT FOUND”}
      I used precompiled code that You provided here on this website.
      I am using MKS Robin WiFi v1.1

    10. deniz says:

      hi,
      i use esp-07 and wire same pinout like esp wifi module
      i use mks robin nano v1.3 and two trees sapphire pro
      i uploaded wifi bin file via sd card but alwys says wifi is being configured

      best..

    Leave a Reply

    Your email address will not be published. Required fields are marked *