Code

This page is for the better understanding of the firmware of Gepetto.

GepettoWiFi v1.0

The first complete version of NodeMCU part of the Gepetto.

//GepettoWiFi v1.0 by Levitate
//Last Update 28.06.2019
/***********************************************************************/
//NodeMCU sleeps until a pulse comes to its reset pin
//Sends a 'R'eady message on the serial and waits for a 'C'onnect order
//Connects to the internet on the declared ssid and password
//Sends a 'C'onnected message on
//Reads and stores the data on the json file at the declared url
//Waits for 'S'end order to send the data on the serial
//Sends the data and goes back to sleep

#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <ArduinoJson.h>

// WiFi Parameters
const char* ssid = "Levitate_Gidasi";
const char* password = "uranyumgidasi";
const char* urlJson = "http://levitate.com.tr/stepper.json";

//The time allowed to wait for the internet connection to be established
//This is generally much more then the messageWaitingLimit
const int wifiWaitingLimit = 30000;
const int messageWaitingLimit = 2000;
unsigned long initialTime = millis();
bool waitingFlag = true;

void setup() {
  Serial.begin(9600);   
}

void loop() {
  //Send a 'R'eady signal to arduino so it can ask for internet connection
  Serial.write('R');
  initialTime = millis();
  
  //Wait for a 'C'onnect answer for an interval of messageWaitingLimit
  while((waitingFlag) && ((millis() - initialTime) < messageWaitingLimit)){
    if(Serial.read() == 'C')
      waitingFlag = false;
    yield();
  }
  
  //If 'C'onnect is acknowledged, start internet connection
  if(waitingFlag == false){
    waitingFlag = true;   
    WiFi.begin(ssid, password);   
    initialTime = millis(); 
    while (waitingFlag && ((millis() - initialTime) < wifiWaitingLimit)){
      if(WiFi.status() == WL_CONNECTED){
        waitingFlag = false;   
      }
      yield();
    }
        //If the connection is established send a 'C'onnected signal to serial
        if(waitingFlag == false){
          waitingFlag = true;
          Serial.write('C');       
               
          //Read and store the data from the server      
          HTTPClient http;
          http.begin(urlJson);
          int httpCode = http.GET();
          if (httpCode > 0){
            //Buffer capacity is decided according to the data on the server
            //This data can be calculated automatically on the https://arduinojson.org/v6/assistant/
            const size_t capacity = 8*JSON_ARRAY_SIZE(4) + JSON_OBJECT_SIZE(8) + 270;
            DynamicJsonBuffer jsonBuffer(capacity);
            JsonObject& root = jsonBuffer.parseObject(http.getString());
            
            //Debugging part to see data read from the server
            /*
            JsonArray& ms_delays_at_the_start = root["ms_delays_at_the_start"];
            int ms_delays_at_the_start_0 = ms_delays_at_the_start[0]; // 0
            int ms_delays_at_the_start_1 = ms_delays_at_the_start[1]; // 1200
            int ms_delays_at_the_start_2 = ms_delays_at_the_start[2]; // 0
            int ms_delays_at_the_start_3 = ms_delays_at_the_start[3]; // 0
            
            JsonArray& max_speeds1 = root["max_speeds1"];
            int max_speeds1_0 = max_speeds1[0]; // 372
            int max_speeds1_1 = max_speeds1[1]; // 372
            int max_speeds1_2 = max_speeds1[2]; // 0
            int max_speeds1_3 = max_speeds1[3]; // 0
            
            JsonArray& accels1 = root["accels1"];
            int accels1_0 = accels1[0]; // 2000
            int accels1_1 = accels1[1]; // 2000
            int accels1_2 = accels1[2]; // 0
            int accels1_3 = accels1[3]; // 0
            
            JsonArray& ms_delays_at_the_end_of_fig1 = root["ms_delays_at_the_end_of_fig1"];
            int ms_delays_at_the_end_of_fig1_0 = ms_delays_at_the_end_of_fig1[0]; // 1200
            int ms_delays_at_the_end_of_fig1_1 = ms_delays_at_the_end_of_fig1[1]; // 1200
            int ms_delays_at_the_end_of_fig1_2 = ms_delays_at_the_end_of_fig1[2]; // 0
            int ms_delays_at_the_end_of_fig1_3 = ms_delays_at_the_end_of_fig1[3]; // 0
            
            JsonArray& move_to_meters1 = root["move_to_meters1"];
            int move_to_meters1_0 = move_to_meters1[0]; // 0
            int move_to_meters1_1 = move_to_meters1[1]; // 0
            int move_to_meters1_2 = move_to_meters1[2]; // 0
            int move_to_meters1_3 = move_to_meters1[3]; // 0
            
            JsonArray& max_speeds2 = root["max_speeds2"];
            int max_speeds2_0 = max_speeds2[0]; // 372
            int max_speeds2_1 = max_speeds2[1]; // 372
            int max_speeds2_2 = max_speeds2[2]; // 0
            int max_speeds2_3 = max_speeds2[3]; // 0
            
            JsonArray& accels2 = root["accels2"];
            int accels2_0 = accels2[0]; // 2000
            int accels2_1 = accels2[1]; // 2000
            int accels2_2 = accels2[2]; // 0
            int accels2_3 = accels2[3]; // 0
            
            JsonArray& ms_delays_at_the_end_of_fig2 = root["ms_delays_at_the_end_of_fig2"];
            int ms_delays_at_the_end_of_fig2_0 = ms_delays_at_the_end_of_fig2[0]; // 1200
            int ms_delays_at_the_end_of_fig2_1 = ms_delays_at_the_end_of_fig2[1]; // 1200
            int ms_delays_at_the_end_of_fig2_2 = ms_delays_at_the_end_of_fig2[2]; // 0
            int ms_delays_at_the_end_of_fig2_3 = ms_delays_at_the_end_of_fig2[3]; // 0
            */
                        
            //Wait for a 'S'end signal on the serial before sending the data
            //'S'end message takes more time to come then the other messages 
            initialTime = millis();
            while(waitingFlag && ((millis() - initialTime) < messageWaitingLimit)){
              if(Serial.read() == 'S'){
                waitingFlag = false;
                root.printTo(Serial);
                Serial.flush();
              }
              yield();
            }
            waitingFlag = true;        
          }
  
          //End the http connection disconnect from the internet
          http.end();
      }
        WiFi.disconnect(true);
  }   
  //Go back to deepsleep
  ESP.deepSleep(0);
}

Before using be sure that the ssid and the password are the ones you want to use. Debugging on the serial monitor is not possible for this part, because NodeMCU uses its only Tx and Rx pins to communicate with the other device. LEDs might be a solution for that. If you want to see the data received from the server, you can use SoftwareSerial or extract the parts where the data transfer takes place. wifiWaitingLimit and messageWaitingLimit variables are decided on the debug process and might be problematic for the larger data. In that case increasing those values will solve the problem.

Unplug Arduino from USB when you update the firmware in NodeMCU.

Don't forget to check the port selection

Update Choreograpy

Arduino part of the updateChoreography function

//Wakes up NodeMCU, gets the new data from serial, 
//overwrites this data to eeprom and global variables of the 3rd choreography
void updateChoreography(){
    
    //Give a pulse to wake NodeMCU
    digitalWrite(SLEEP_PIN, HIGH);
    delay(500);
    digitalWrite(SLEEP_PIN, LOW);
    
    //Wait until NodeMCU start. It will send 'R' when it starts
    initialTime = millis();
    while(waitingFlag && ((millis() - initialTime) < messageWaitingLimit)){
      if(Serial3.read() == 'R')
        waitingFlag = false;
    }

    //Send 'C' to NodeMCU and it will connect to the internet
    if(waitingFlag == false){
      waitingFlag = true;
      Serial3.write('C');     
      Serial.println("NodeMCU ready");
      Serial.println("Waiting for internet connection");
      initialTime = millis();
      
      //Wait a 'C'onnected message from NodeMCU
      while(waitingFlag && ((millis() - initialTime) < wifiWaitingLimit)){
        if(Serial3.read() == 'C'){
          waitingFlag = false;
        }
        yield();
      }
      
      //If NodeMCU is connected to the internet, send a request for the data that NodeMCU read from the server
      if(waitingFlag == false){
        waitingFlag = true;
        Serial3.write('S');
        Serial.println("Connected");
        Serial.println("Waiting for data");
      //Read the json object into root. If the json object is invalid return to the Idle state
      StaticJsonBuffer<1000> jsonBuffer;
      JsonObject& root = jsonBuffer.parseObject(Serial3);
        if(root != JsonObject::invalid()){

      //If the json file is correct, write the data to eeprom
      Serial.println("JSON received and parsed");
      eepromAddress = 0;
      for(int i=0; i<num_of_motors; i++){
        unsigned long eepromBuffer = root["ms_delays_at_the_start"][i];
        Serial.println(eepromBuffer);
        EEPROM.put(eepromAddress, eepromBuffer);
        eepromAddress += sizeof(root["ms_delays_at_the_start"][i]);
      }
      for(int i=0; i<num_of_motors; i++){
        float eepromBuffer = root["max_speeds1"][i];
        Serial.println(eepromBuffer);
        EEPROM.put(eepromAddress, eepromBuffer);
        eepromAddress += sizeof(root["max_speeds1"][i]);
      }
      for(int i=0; i<num_of_motors; i++){
        float eepromBuffer = root["accels1"][i];
        Serial.println(eepromBuffer);
        EEPROM.put(eepromAddress, eepromBuffer);
        eepromAddress += sizeof(root["accels1"][i]);
      }
      for(int i=0; i<num_of_motors; i++){
        unsigned long eepromBuffer = root["ms_delays_at_the_end_of_fig1"][i];
        Serial.println(eepromBuffer);
        EEPROM.put(eepromAddress, eepromBuffer);
        eepromAddress += sizeof(root["ms_delays_at_the_end_of_fig1"][i]);  
      }
      for(int i=0; i<num_of_motors; i++){
        float eepromBuffer = root["move_to_meters1"][i];
        Serial.println(eepromBuffer);
        EEPROM.put(eepromAddress, eepromBuffer);
        eepromAddress += sizeof(root["move_to_meters1"][i]);  
      }
      for(int i=0; i<num_of_motors; i++){
        float eepromBuffer = root["max_speeds2"][i];
        Serial.println(eepromBuffer);
        EEPROM.put(eepromAddress, eepromBuffer);
        eepromAddress += sizeof(root["max_speeds2"][i]); 
      }
      for(int i=0; i<num_of_motors; i++){
        float eepromBuffer = root["accels2"][i];
        Serial.println(eepromBuffer);
        EEPROM.put(eepromAddress, eepromBuffer);
        eepromAddress += sizeof(root["accels2"][i]);   
      }
      for(int i=0; i<num_of_motors; i++){
        unsigned long eepromBuffer = root["ms_delays_at_the_end_of_fig2"][i];
        Serial.println(eepromBuffer);
        EEPROM.put(eepromAddress, eepromBuffer);
        eepromAddress += sizeof(root["ms_delays_at_the_end_of_fig2"][i]);     
      }
      for(int i=0; i<num_of_motors; i++){
        float eepromBuffer = root["move_to_meters2"][i];
        Serial.println(eepromBuffer);
        EEPROM.put(eepromAddress, eepromBuffer);
        eepromAddress += sizeof(root["move_to_meters2"][i]);      
      }
      //Read the data from eeprom into the arrays that will be used by the motion function
      readFromEeprom(ms_delays_at_the_start3,   
                    ms_delays_at_end_of_fig13, 
                    ms_delays_at_end_of_fig23, 
                    max_speeds13, 
                    max_speeds23, 
                    accels13, 
                    accels23, 
                    move_to_meters13, 
                    move_to_meters23);
       //Debugging part (check if the values are read from eeprom)
       /*
       Serial.println("ms_delays_at_the_start3");
       for(int i = 0; i<num_of_motors; i++){
          Serial.println(ms_delays_at_the_start3[i]);
       }
       Serial.println("ms_delays_at_end_of_fig13");
       for(int i = 0; i<num_of_motors; i++){
          Serial.println(ms_delays_at_end_of_fig13[i]);
       }
       Serial.println("ms_delays_at_end_of_fig23");
       for(int i = 0; i<num_of_motors; i++){
          Serial.println(ms_delays_at_end_of_fig23[i]);
       }
       Serial.println("max_speeds13");
       for(int i = 0; i<num_of_motors; i++){
          Serial.println(max_speeds13[i]);
       }
       Serial.println("max_speeds23");
       for(int i = 0; i<num_of_motors; i++){
          Serial.println(max_speeds23[i]);
       }
       Serial.println("accels13");
       for(int i = 0; i<num_of_motors; i++){
          Serial.println(accels13[i]);
       }
       Serial.println("accels23");
       for(int i = 0; i<num_of_motors; i++){
          Serial.println(accels23[i]);
       }
       Serial.println("move_to_meters13");
       for(int i = 0; i<num_of_motors; i++){
          Serial.println(move_to_meters13[i]);
       }
       Serial.println("move_to_meters23");
       for(int i = 0; i<num_of_motors; i++){
          Serial.println(move_to_meters23[i]);
       }
      */
      meter_to_steps(move_to_meters13, move_to_meters23, 3);
      Serial.println("Finished");
        }
        else
        Serial.println("Jason invalid");
      }
      else
      Serial.println("Internet connection failed");
    }
    else
    Serial.println("NodeMCU is still asleep");
    
}

Read From EEPROM

void readFromEeprom(unsigned long ms_delays_at_the_start[num_of_motors],   
            unsigned long ms_delays_at_end_of_fig1[num_of_motors], 
            unsigned long ms_delays_at_end_of_fig2[num_of_motors], 
            float max_speeds1[num_of_motors], 
            float max_speeds2[num_of_motors], 
            float accels1[num_of_motors], 
            float accels2[num_of_motors],
            float move_to_meters1[num_of_motors],
            float move_to_meters2[num_of_motors])
       {
        eepromAddress = 0;
        for(int i=0; i<num_of_motors; i++){
          EEPROM.get(eepromAddress, ms_delays_at_the_start[i]);
          eepromAddress += sizeof(ms_delays_at_the_start[i]);
        }
        for(int i=0; i<num_of_motors; i++){
          EEPROM.get(eepromAddress, max_speeds1[i]);
          eepromAddress += sizeof(max_speeds1[i]);
        }
        for(int i=0; i<num_of_motors; i++){
          EEPROM.get(eepromAddress, accels1[i]);
          eepromAddress += sizeof(accels1[i]);
        }
        for(int i=0; i<num_of_motors; i++){
          EEPROM.get(eepromAddress, ms_delays_at_end_of_fig1[i]);
          eepromAddress += sizeof(ms_delays_at_end_of_fig1[i]);  
        }
        for(int i=0; i<num_of_motors; i++){
          EEPROM.get(eepromAddress, move_to_meters1[i]);
          eepromAddress += sizeof(move_to_meters1[i]);  
        }
        for(int i=0; i<num_of_motors; i++){
          EEPROM.get(eepromAddress, max_speeds2[i]);
          eepromAddress += sizeof(max_speeds2[i]); 
        }
        for(int i=0; i<num_of_motors; i++){
          EEPROM.get(eepromAddress, accels2[i]);
          eepromAddress += sizeof(accels2[i]);   
        }
        for(int i=0; i<num_of_motors; i++){
          EEPROM.get(eepromAddress, ms_delays_at_end_of_fig2[i]);
          eepromAddress += sizeof(ms_delays_at_end_of_fig2[i]);     
        }
        for(int i=0; i<num_of_motors; i++){
          EEPROM.get(eepromAddress, move_to_meters2[i]);
          eepromAddress += sizeof(move_to_meters2[i]);      
        }
}

When updating, we write the data to EEPROM to store when the system is off. Every time the system starts or updates, we need to read data from EEPROM and write those values into the arrays of respective choreography parameters. As we write this data starting from the address "0", we need to load it back in the same order.

Waking Up NodeMCU

Since we do not want NodeMCU to be up and running all the time we opt to put it into deep sleep mode. This is beneficial in two ways:

  1. It consumes very little power (draws only 20 nA and that is for the real time clock)

  2. It does not wear up or get warm. Also, this way, we make sure that whenever we ask for internet connection and data transfer, NodeMCU will start with reset.

Putting it into sleep mode:

  ESP.deepSleep(0);

Easy as pie! But waking it back up requires special attention. Basically, NodeMCU needs to see LOW signal at its reset pin. This breaks up the waking cycle and put it back on. However, we need to pull reset pin low with a signal from our Arduino Mega. Connecting a pin directly does not solve the problem because NodeMCU pulls it high.

To solve this issue we use a common emitter switch.

In our case, since Rb is 10 times smaller then Rc, when PIN22 is held HIGH, transistor turns on and gets into saturation so that most of the current goes through Rc. RST pin on NodeMCU is shorted to ground. So now, all we need to do to wake NodeMCU up is run the following code.

digitalWrite(SLEEP_PIN, HIGH);
delay(500);
digitalWrite(SLEEP_PIN, LOW);

Last updated