Week 14 - Networking and communications

Assignment

Send a message between two projects

For this week's group assignment, Harm and I wanted to communicate each other through a server like below.

When a student presses a button, it will send an API request to the server. And the other side, there will be an output device fetching the data from the server and it will update the state of an output device. There are only two endpoints on the server, one for updating the boolean state, and the other one is for getting the data.

To get the button state of a student: curl -X GET https://groupapi-fabacadey.herokuapp.com/buttonState/{student_name}

To update/create a button state : curl --data "name={student_name}&buttonStatus={boolean}" https://groupapi-fabacadey.herokuapp.com/buttonStatus

Hyejin gave me another Json URL to test with: https://jsonplaceholder.typicode.com/todos/1

I separated this URL and changed these lines:

Hyejin - Made an API endpoint

I setup an API server so that other students can fetch the data from the server. I deployed on Heroku so that everyone could access to it. I created an endpoint for each student like below. The entire repo can be downloaded from here.

   
    # https://devhints.io/sequel
    require 'sequel'
    require 'dotenv'
    require 'sinatra'
    Dotenv.load

    SEQ = Sequel.connect ENV.fetch("DATABASE_URL")



    post "/buttonStatus" do
    insert_params = {name: params.fetch("name"), buttonStatus: params.fetch("buttonStatus")}

    SEQ[:users]
    .insert_conflict(constraint: :users_pkey, update: insert_params)
    .insert(insert_params)
    end


    get "/buttonState/:user" do 
    user = SEQ[:users].where(:name => params.fetch(:user)).first
    {name: user[:name], buttonStatus: user[:buttonStatus]}.to_json
    end


    get '/' do
    redirect '/index.html'
    end
  

https://groupapi-fabacadey.herokuapp.com/buttonState/harm

   
  curl --data "name={name}&buttonStatus=false" https://groupapi-fabacadey.herokuapp.com/buttonStatus
  curl --data "name={name}&buttonStatus=true" https://groupapi-fabacadey.herokuapp.com/buttonStatus

Harm - getting data from the server and turn the LED on

if (!client.connect("jsonplaceholder.typicode.com", 80)) { client.println(F("GET /todos/1 HTTP/1.0")); client.println(F("Host: jsonplaceholder.typicode.com"));

Reading data from Hyejin over the internet

In the meantime Hyejin managed to finish the write part on her server. She told me I can change the buttonStatus value by executing this in Terminal:

curl --data "name=harm&buttonStatus=false" https://groupapi-fabacadey.herokuapp.com/buttonStatus curl --data "name=harm&buttonStatus=true" https://groupapi-fabacadey.herokuapp.com/buttonStatus

Code example for the receiver

   
    #include <ArduinoJson.h>
    #include <ESP8266WiFi.h>
    #include <ESP8266WiFiMulti.h>
    #ifndef STASSID
    #define STASSID "Hutspot" //Change this
    #define STAPSK  "88888888" //Change this
    #endif
    const char* ssid     = STASSID;
    const char* password = STAPSK;
    ESP8266WiFiMulti WiFiMulti;
    void setup() {
    pinMode(D0, OUTPUT);
    // Initialize Serial port
    Serial.begin(9600);
    Serial.print("testing 123");
    // connecting to a WiFi network
    WiFi.mode(WIFI_STA);
    WiFiMulti.addAP(ssid, password);
    Serial.println();
    Serial.println();
    Serial.print("Wait for WiFi... ");
    while (WiFiMulti.run() != WL_CONNECTED) {
    Serial.print(".");
    delay(500);
  }
  Serial.println("");  Serial.println("WiFi connected");  Serial.println("IP address: ");  Serial.println(WiFi.localIP());
}
void loop() {
// Connect to HTTP server
WiFiClient client;
client.setTimeout(10000);
if (!client.connect("groupapi-fabacadey.herokuapp.com", 80)) {
Serial.println(F("Connection failed"));
return;
}
Serial.println(F("Connected!"));
// Send HTTP request
client.println(F("GET /buttonState/harm HTTP/1.0")); //Change this, harm in tessel or nathan
client.println(F("Host: groupapi-fabacadey.herokuapp.com"));
client.println(F("Connection: close"));
if (client.println() == 0) {
Serial.println(F("Failed to send request"));
return;
}
// Check HTTP status
char status[32] = {0};
client.readBytesUntil('\r', status, sizeof(status));
// It should be "HTTP/1.0 200 OK" or "HTTP/1.1 200 OK"
if (strcmp(status + 9, "200 OK") != 0) {
Serial.print(F("Unexpected response: "));
Serial.println(status);
return;
}
// Skip HTTP headers
char endOfHeaders[] = "\r\n\r\n";
if (!client.find(endOfHeaders)) {
Serial.println(F("Invalid response"));
return;
}
// Allocate the JSON document
// Use arduinojson.org/v6/assistant to compute the capacity.
const size_t capacity = JSON_OBJECT_SIZE(3) + JSON_ARRAY_SIZE(2) + 60;
DynamicJsonDocument doc(capacity);
// Parse JSON object
DeserializationError error = deserializeJson(doc, client);
if (error) {
Serial.print(F("deserializeJson() failed: "));  Serial.println(error.c_str());
return;
}
// Extract values
Serial.println(F("Response:"));
Serial.println(doc["name"].as<char*>());
Serial.println(doc["buttonStatus"].as<bool>());
bool buttonS = doc["buttonStatus"].as<bool>();
if (buttonS == 1) {
digitalWrite(D0, HIGH); //Change this to the output you like
} else {
digitalWrite(D0, LOW); //Change this to the output you like
}
// Disconnect
client.stop();
delay (10000);
}

Heroshot of Harm

Nathan and Tessel: connecting devices over WiFi using mesh networking.

For the group assignment, Tessel and I created a mesh network between our two ESP-32 boards. The ESP-32 are WiFi enabled so we connected wirelessly.

I downloaded the 'Painless Mesh' library from the Arduino library manager, which also asked that I download the task manager library.

After downloading this, I opened the Basic example sketch from the painless mesh library. Tessel had different code on her device. The codes are added below.  In this example sketch, we only had to change our username and password to the same network, as well as make sure we listed the same port.

Upon upload to both of our individual boards, we then opened the serial monitors on our respective computers. The monitor on each of our computers showed that a connection was established. Tessel's monitor then began to display messages from my ESP, which were randomly generated every 5 seconds.

Here are pictures and a video from our setup:

Here is our setup. Nathan's is on the left with his device next to it. It is connected to the USB port using an FTDI. My computer is on the right with my board on top of the keyboard.  

Here you can see Nathan's serial monitor. You can see that the chip has established a network and that it sees a new node. That is my device.  

Here is Tessel’s serial monitor displaying each time it receives a message from Nathan's node.

Here is a video of the setup. We pan from my device, to Nathan's device. Then to Nathan's serial monitor showing that the network is established. And ending on my serial monitor showing messages being received.    

<iframe src="https://player.vimeo.com/video/435007011?byline=0&portrait=0" width="640" height="360" frameborder="0" allow="autoplay; fullscreen" allowfullscreen></iframe>

Code on Nathan’s device:

//************************************************************

// this is a simple example that uses the painlessMesh library

//

// 1. sends a silly message to every node on the mesh at a random time between 1 and 5 seconds

// 2. prints anything it receives to Serial.print

//

//

//************************************************************

#include "painlessMesh.h"

#define   MESH_PREFIX         "Post-collapseNetwork"

#define   MESH_PASSWORD   "TechnoLogic"

#define   MESH_PORT           5555

Scheduler userScheduler; // to control your personal task

painlessMesh  mesh;

// User stub

void sendMessage() ; // Prototype so PlatformIO doesn't complain

Task taskSendMessage( TASK_SECOND * 1 , TASK_FOREVER, &sendMessage );

void sendMessage() {

  String msg = "Hello from node ";

  msg += mesh.getNodeId();

  mesh.sendBroadcast( msg );

  taskSendMessage.setInterval( random( TASK_SECOND * 1, TASK_SECOND * 5 ));

}

// Needed for painless library

void receivedCallback( uint32_t from, String &msg ) {

  Serial.printf("startHere: Received from %u msg=%s\n", from, msg.c_str());

}

void newConnectionCallback(uint32_t nodeId) {

        Serial.printf("--> startHere: New Connection, nodeId = %u\n", nodeId);

}

void changedConnectionCallback() {

  Serial.printf("Changed connections\n");

}

void nodeTimeAdjustedCallback(int32_t offset) {

        Serial.printf("Adjusted time %u. Offset = %d\n", mesh.getNodeTime(),offset);

}

void setup() {

  Serial.begin(115200);

//mesh.setDebugMsgTypes( ERROR | MESH_STATUS | CONNECTION | SYNC | COMMUNICATION | GENERAL | MSG_TYPES | REMOTE ); // all types on

  mesh.setDebugMsgTypes( ERROR | STARTUP );  // set before init() so that you can see startup messages

  mesh.init( MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT );

  mesh.onReceive(&receivedCallback);

  mesh.onNewConnection(&newConnectionCallback);

  mesh.onChangedConnections(&changedConnectionCallback);

  mesh.onNodeTimeAdjusted(&nodeTimeAdjustedCallback);

  userScheduler.addTask( taskSendMessage );

  taskSendMessage.enable();

}

void loop() {

  // it will run the user scheduler as well

  mesh.update();

}

Code on Tessel’s device:

// Code based on the ESP32 example code TouchInterupt and code from

// https://iotdesignpro.com/projects/creating-nodemcu-mesh-networking-using-esp12-and-arduino-ide

//Touchpin numberbering can be found at https://gitlab.fabcloud.org/barcelonaworkshops/barduino-2.0/

// Program runs painlessMesh to create a mesh network

// Operates 5 touchpins. When pin is touched a message is send to both the serial monitor and the oled monitor

// When a message is received it outputs to both serial and oled monitor.

// position of cursor is communicated to the monitor.

// When cursor is at the end of the monitor, display.cleardisplay is triggered.  

// Code for Node1:

#include "painlessMesh.h"   // include painlessMesh library

#include <ArduinoJson.h>

#include <SPI.h>

#include <Wire.h>

#include <Adafruit_GFX.h>

#include <Adafruit_SSD1306.h>

// WiFi Credentials

#define   MESH_PREFIX         "Post-collapseNetwork"

#define   MESH_PASSWORD   "TechnoLogic"

#define   MESH_PORT           5555

#define SCREEN_WIDTH 128 // OLED display width, in pixels

#define SCREEN_HEIGHT 64 // OLED display height, in pixels

// Necessary for OLED Display

#define OLED_RESET         -1 // Reset pin # (or -1 if sharing Arduino reset pin)

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

int threshold = 23; // setting threshold for detecting touch on pin

// int threshold2 = 99;

bool touch1detected = false;  

bool touch2detected = false;

bool touch3detected = false;

bool touch4detected = false;

bool touch5detected = false;

bool touch6detected = false;

//Pin Declaration

#define touchPin1 T2  // declaring touch pins

#define touchPin2 T6

#define touchPin3 T7

#define touchPin4 T8

#define touchPin5 T3

#define touchPin6 T5

Scheduler userScheduler; // calling on the scheduler to control your personal task. This replaces 'delay' function which messes up painlessMesh

painlessMesh  mesh;  // calling on painlessMesh

void sendMessage() ;  // Prototype so PlatformIO doesn't complain

Task taskSendMessage( TASK_SECOND * 1 , TASK_FOREVER, &sendMessage );  // this command replaces the 'delay' function

void sendMessage()

{

// Reading Status of touchPin

  if (digitalRead(touchPin1) == HIGH)

        touch1detected = !touch1detected;  // creates container to put status of pin in.

  if (digitalRead(touchPin2) == HIGH)

        touch2detected = !touch2detected;

  if (digitalRead(touchPin3) == HIGH)

        touch3detected = !touch3detected;

  if (digitalRead(touchPin4) == HIGH)

        touch4detected = !touch4detected;

  if (digitalRead(touchPin5) == HIGH)

        touch5detected = !touch5detected;

  if (digitalRead(touchPin6) == HIGH)

        touch6detected = !touch6detected;

   

// Serializing in JSON Format

  DynamicJsonDocument doc(1024);

  doc["Relay1"] = touch1detected; // container created above is made into a doc named Relay1 file.

  doc["Relay2"] = touch2detected; // container created above is made into a doc named Relay2 file.

  doc["Relay3"] = touch3detected;

  doc["Relay4"] = touch4detected;

  doc["Relay5"] = touch5detected;

  doc["Relay6"] = touch6detected;

  String msg ;

  serializeJson(doc, msg);   //

        }

// Needed for painless library

void receivedCallback( uint32_t from, String &msg ) {

  Serial.printf("startHere: Received from %u msg=%s\n", from, msg.c_str());

  checkDisplay();

  display.print(msg.c_str());

  display.display();

}

void newConnectionCallback(uint32_t nodeId) {

  Serial.printf("--> startHere: New Connection, nodeId = %u\n", nodeId);

}

void changedConnectionCallback() {

  Serial.printf("Changed connections\n");

}

void nodeTimeAdjustedCallback(int32_t offset) {

  Serial.printf("Adjusted time %u. Offset = %d\n", mesh.getNodeTime(), offset);

}

void gotTouch1(){

 touch1detected = true;

}

void gotTouch2(){

 touch2detected = true;

}

void gotTouch3(){

 touch3detected = true;

}

void gotTouch4(){

 touch4detected = true;

}

void gotTouch5(){

 touch5detected = true;

}

void gotTouch6(){

 touch6detected = true;

}

void setup() {

  Serial.begin(115200);

  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);// initialize with the I2C addr 0x3C (for the 128x64)

  display.clearDisplay();

  display.setTextSize(1);

  display.setTextColor(WHITE);

  display.setCursor(0, 0);

  display.println("post-collapse network");

  display.setTextSize(1);

  display.setCursor(0, 15);

  display.print("Message: ");

  display.setTextColor(WHITE); // 'inverted' text

  display.display();   //you have to tell the display to...display

 

  touchAttachInterrupt(T2, gotTouch1, threshold);  // checks if touchpin is interupted

  touchAttachInterrupt(T6, gotTouch2, threshold);

  touchAttachInterrupt(T7, gotTouch3, threshold);

  touchAttachInterrupt(T8, gotTouch4, threshold);

  touchAttachInterrupt(T3, gotTouch5, threshold);

  touchAttachInterrupt(T5, gotTouch6, threshold);

 

  mesh.setDebugMsgTypes( ERROR | STARTUP );  // set before init() so that you can see startup messages

  mesh.init( MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT );

  mesh.onReceive(&receivedCallback);

  mesh.onNewConnection(&newConnectionCallback);

  mesh.onChangedConnections(&changedConnectionCallback);

  mesh.onNodeTimeAdjusted(&nodeTimeAdjustedCallback);

  userScheduler.addTask( taskSendMessage );

  taskSendMessage.enable();

}

void checkDisplay(){

   // int cursorX=display.getCursorX();

   // Serial.println(cursorX);

        int cursorY=display.getCursorY();

        Serial.println(cursorY);

        if(cursorY>53){

          display.clearDisplay();

          display.setCursor(0, 0);

        }

 

}

void loop() {

        mesh.update();

   

 

  if(touch1detected){

        touch1detected = false;

        checkDisplay();

        Serial.println("Touch 1 detected");

        display.print("/");

        display.display();

   

        sendMessage();   // Broadcasting message

        String msg = "/";

        mesh.sendBroadcast( msg );

}

 

  if(touch2detected){

        touch2detected = false;

        checkDisplay();

        Serial.println("Touch 2 detected");

        display.print("-");

        display.display();

  sendMessage();  

        String msg = "-";

        mesh.sendBroadcast( msg );  

  }

 if(touch3detected){

        touch3detected = false;

        checkDisplay();

        Serial.println("Touch 3 detected");

        display.print(" ");

        display.display();

  sendMessage();  

        String msg = " ";

        mesh.sendBroadcast( msg );

 }    

 if(touch4detected){

        touch4detected = false;

        checkDisplay();

        Serial.println("Touch 4 detected");

        display.print(".");

        display.display();

  sendMessage();  

        String msg = ".";

        mesh.sendBroadcast( msg );

 }    

 if(touch5detected){

        touch5detected = false;

        checkDisplay();

        Serial.println("Touch 5 detected");

        display.print("/");

        display.display();

  sendMessage();  

        String msg = "/";

        mesh.sendBroadcast( msg );

 }

 

 if(touch6detected){

        touch6detected = false;

        checkDisplay();

        Serial.println("Touch 6 detected");

        display.print("/");

        display.display();

  sendMessage();  

        String msg = "/";

        mesh.sendBroadcast( msg );