Ho acquistato un FlyingBear Ghost 5 con modulo WiFi integrato, ma scopro che non c’è l’interfaccia web. Non so perché non possano aggiungere un’interfaccia utente di base. Poi sono andato a vedere l’interfaccia web delle schede di fascia alta della Makerbase e ho capito che era meglio che non l’avessero sviluppata.
La mia soluzione è stata modificare il firmware per supportare il Web Socket e sviluppare l’interfaccia Web. Questa funzione è compatibile con tutte le schede Makerbase che dispongono di un modulo WiFi MKS.
Il risultato dell’interfaccia Web di BeePrint è in questa schermata.
Ho anche deciso di spiegare tutte le fasi dello sviluppo del progetto ed in quest’articolo qui vorrei esporre l’infrastruttura hardware della scheda MKS WiFi che si interfaccia con la Makerbase Robin Nano.
Ora analizzeremo le modifiche che ho apportato al modulo WiFi MKS originale, con l’introduzione del Web Socket e alcune piccole funzionalità per sviluppare l’interfaccia Web ed eseguire il debug.
Aggiornamento WiFi MKS
Ho apportato alcune modifiche al codice originale, le vecchie funzioni sono rimaste le stesse, ma devo aggiungere un server WebSocket e alcuni endpoint per funzionalità aggiuntive.
Ma queste modifiche utilizzano un po’ di memoria aggiuntiva, quindi abbiamo perso l’aggiornamento OTA per il filesystem, quando dobbiamo caricare nuovamente il filesystem, dobbiamo eseguire il downgrade del firmware, caricare il filesystem e caricare di nuovo il firmware.
Codice WebSocket
La sezione che gestisce il messaggio è questa
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;
}
Ho isolato il codice in modo da poter chiamare questa funzione nella zona di gestione precedente, qui la gestione dei messaggi TCP.
{
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;
}
}
}
e qui la gestione nel listener WS.
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
E’ possibile connettersi con il semplice client websocket che puoi trovare in questo articolo.
Quando la connessione è iniziata, ricevi le informazioni sulla temperatura e sul WiFi, puoi provare ad aggiungere un comando come M20 1:
, fai attenzione al ritorno alla fine (usa shift + invio), e riceverai una serie di messaggi come
End point REST
Per impostazione predefinita, viene esposto solo un end-point, il caricamento del file.
Per sviluppare un’interfaccia Web prima abiliterò le richieste CORS, per maggiori informazioni fare riferimento a questo tutorial “Server REST con esp8266 e esp32: richieste CORS, OPTION e POST“.
Dove sendCrossOriginHeader è gestito dalla define ENABLE_CORS
.
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);
}
Come puoi vedere, non è un multipart form data, ma vuole un flusso del file in formato raw.
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;
}
}
}
Aggiungo anche un nuovo end point in GET e POST per gestire la configurazione di BeePrint:
server.on("/configApp", HTTP_OPTIONS, sendCrossOriginHeader);
server.on("/configApp", HTTP_POST, postConfigFile, NULL);
server.on("/configApp", HTTP_GET, getConfigFile, NULL);
Memorizza un file su SPIFFS che può essere utilizzato per preservare la configurazione dell’interfaccia utente di BeeTree.
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!"));
}
}
}
}
Caricamento di prova
Il processo di caricamento può essere gestito dal un comando curl come questo:
curl -i -X POST "http://192.168.1.164/upload?X-Filename=prova.stl" -H "Content-Type: text/xml" --data-binary "@FBG5_prova.gcode"
E qui la risposta:
* 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
Compilare la versione mischianti del firmware MKS WiFi
Ora dobbiamo compilare la versione Mischianti MKS WiFi del firmware, puoi caricarla direttamente sulla scheda WiFi MKS o sul dispositivo NodeMCU come descritto nell’articolo precedente.
Prima di tutto devi usare la versione 2.7.4 del core esp8266, perché continuo a usare il filesystem SPIFFS come la versione originale.
Quindi devi impostare questo valore sul tuo IDE Arduino:
- Consiglio : NodeMCU 1.0 (ESP-12E Module)
- Led integrato : 2
- Velocità di caricamento : 921600 (115200 è più sicuro)
- Frequenza CPU : “160 MHz” (80 MHz è più sicuro)
- Dimensione flash : 4 MB
- Spiffs : 3Mb
E la mia ultima build usa questo set di librerie
- ArduinoJson@6.18.4
- ESP8266WiFi@1.0
- ESP8266mDNS@1.2
- EEPROM@1.0
- ESP8266HTTPClient@1.2
- WebSocket@2.3.5
Attiva CORS e DEBUG
Per semplificare lo sviluppo del firmware aggiungo 2 define:
#define ENABLE_CORS
che consentono di utilizzare un’interfaccia web esterna (non installata sul dispositivo), puoi ottenere maggiori informazioni leggendo il tutorial REST .
#define MKSWIFI_DEBUG
Che abilitano alcune informazioni di debug su una seriale specifica
// Define where debug output will be printed.
#define DEBUG_PRINTER Serial1
con questa configurazione puoi leggere il messaggio di debug sul pin D4, e puoi ottenere maggiori informazioni su questo articolo “WeMos D1 mini (esp8266): eseguire un debug sulla seriale secondaria“.
Generare il file binario FileSystem
Per far funzionare il tutto è necessario generare anche il file binario del filesystem.
Aggiungere un output dettagliato all’IDE di Arduino
Per comprendere meglio tutto il processo, abiliteremo l’output dettagliato sul nostro IDE Arduino. Puoi trovare queste opzioni su File -> Preferences
e controllare i controlli Show verbose output
.
Ora possiamo prendere e riutilizzare i comandi della console.
Prerequisito
Per il filesystem possiamo fare lo stesso, ma prima è necessario leggere la seguente guida WeMos D1 mini (esp8266), SPIFFS Filesystem integrato .
Ora aggiungeremo la directory data
alla cartella degli sketch (con alcuni file) e utilizzeremo il plug-in per caricare.
Nella console troviamo il comando giusto per generare questo file.
Ora possiamo vedere molte informazioni, inclusa la posizione del file e l’indirizzo.
Utilizzare l’interfaccia Web o l’SD per caricare
Ora con l’interfaccia Web puoi seguire questi passaggi per caricare
- Prendi MksWifi_WebView.bin e carica la Web view;
- Prendi MksWifi.bin e carica il firmware.
È importante seguire questi passaggi perché la mia versione del firmware richiede più risorse e se si carica prima il firmware non è possibile caricare il filesystem.
Quindi in futuro se vuoi caricare una nuova versione del filesystem devi caricare la versione precedente del firmware e ripetere il processo.
Puoi scaricare il firmware precompilato della versione Mischianti:
Puoi anche scaricare il firmware originale precompilato:
-
MksWifi.bin
qui;
Se hai un firmware come Flying Bear Ghost 5 puoi mettere questi file all’interno della SD, inserirla nella stampante, riavviarlo, attendere che il primo processo sia stato completato quindi riavviare di nuovo per caricare il file rimanente.
Grazie
- MKS WIFI per Makerbase Robin: schede e come collegare un esp12 o NodeMCU
- MKS WIFI per Makerbase Robin: PCB e come compilare e caricare il firmware
- MKS WIFI per Makerbase Robin: protocollo di comunicazione e plugin Cura
- MKS WIFI per Makerbase Robin: aggiornamento del firmware e nuova funzionalità Web Socket
- MKS WIFI per Makerbase Robin: interfaccia web BeePrint con fotocamera su Flying Bear Ghost