NodeMCU - Arduino Mega Serial Communication
Last updated
Last updated
NodeMCU is in "Deep-sleep" mode when not used to reduce the power consumption and heating. When in deep-sleep mode only the Real Time Clock (RTC) is working and power consumption is approximately 20uA. There are two ways to wake up NodeMCU from deep-sleep. One is by using the timer. In that way we can wake up NodeMCU after a predefined predefined time. However we want to wake up when we will download the new parameters or upload the key frames. To do this we will use the second way which is using an external wake up. NodeMCU wakes up when when a low signal is applied to its reset pin.
Waking up NodeMCU means reset. So it will start executing the sketch from the beginning including the setup() block.
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.
The communication between NodeMCU and Arduino Mega is via Serial and the problem is when we reset the NodeMCU it sends some garbage data over Serial. To deal with this garbage data, NodeMCU sends a meaningful signal (START_SIGNAL = 0x7530) so that Arduino Mega can understand that the garbage data is finished when it reads the start signal over the serial.
Wiring for serial communication between NodeMCU and Arduino Mega is pretty simple. NodeMCU has only one serial communication line (Serial) while Arduino Mega has four ( Serial0, Serial1, Serial2, Serial3). We are using Serial3 at the Arduino Mega side.
NodeMCU | Arduino Mega |
TX | RX3 |
RX | TX3 |
Logic levels of NodeMCU and Arduino Mega are 3.3V and 5V respectively. Connecting the TX-RX pairs directly may damage either (it did not so far). In that case a voltage divider or logic level shifter might be used.
In communication between NodeMCU and Arduino Mega we use some two byte control signals to be sure that the communication is moving on flawlessly. These signals are:
START_SIGNAL = 0x7530
END_SIGNAL = 0x6464
ACK_SIGNAL = 0x4848
MODE_DOWNLOAD_SIGNAL = 0x4040
MODE_UPLOAD_SIGNAL = 0x5050
Control signals are two bytes but the Serial.write() sends one byte at a time. When sending control signals we send the most significant byte first and the least significant byte after that.
After NodeMCU wake up, it sends this information to Arduino Mega and waits for the mode signal. According to the signal received it enters the corresponding mode. Mode parameter is sent by the wakeUpNodeMCU(uint8_t MODE_MSB, uint8_t MODE_LSB) function.
NodeMCU connects to the wifi network with the WiFi.begin(ssid, password) function. Our connectWiFi() function tries to connect to the network specified with the ssid and password. It returns false if the connection is not established in 30 seconds.
The size of the data read from the server is limited by the RAM of the NodeMCU and our data is much larger than this limit. In order to deal with this, we divide the data into multiple json formatted files. First json file defines combination, total frames in all files, number of frames in the current file and whether there is another file or not. Combination and total frames data is not represented in the following files.
In the current version NodeMCU can read 150 frames in a single file. This limit can be increased or needed to be decreased as the sketch chances.
If NodeMCU enters the DOWNLOAD_FRAMES mode after waking up, it executes the sendFramesToMega() function. In this function, NodeMCU connects to the wifi (connectWiFi()), starts an HTTP connection with the server and stores the data as a string (readJson()).
After reading the data into the global string variable. We parse and send it to Arduino Mega (parseAndSendJson()). In this function first of the multiple json files is treated differently as the first file contains combination and total frames data. After those data is sent, we need number of frames in the file to send them correctly. This data is not sent to Arduino Mega and used by NodeMCU itself. After each data sent, NodeMCU waits for an acknowledgement signal to understand that the data is received by Arduino Mega before sending the next data.
Number of frames in the file data is not sent to Arduino Mega. Arduino Mega sees the data as a single file and does not need the number of frames in the file data. However the delay between the frames are a little more when passing to the next file. If this delay exceeds the timeout of readSignal(), the communication will fail after the first file. Increasing the timeout variable of this function may solve the problem.
Message flow of the communication in DOWNLOAD_FRAMES mode can be seen below. Sending frames part loops until all frames are sent.
Message order must be consistent for both NodeMCU and Arduino Mega. Arduino Mega will name the first data received as combination, second as total frames and rest as the frames until the end signal.
If NodeMCU enters the UPLOAD_FRAMES mode after waking up, it executes the readFrameFromMega and sendFrameToServer functions. In readFrameFromMega() function, NodeMCU reads all frames that contain four motor positions via serial and store them in a Recorded_Frames object.
sendFrameToServer function connects to the wifi network, turns that Recorded_Frames object to a JsonObject and starts an HTTP connection with the server just like in the DOWNLOAD_FRAMES scenario. This time we use the POST method to send the data to the server, but before sending we add name and password information so that somebody else cannot change the content in the server.
Message flow of the communication in UPLOAD_FRAMES mode can be seen below. Sending frames part loops until all frames are sent.
http.getString() returns the response payload and http.POST() returns HTTP codes. Using this values may help debugging.