Week 21

Final Project Documentation

Workflow

Here is the flow of the things I did to make this project possible.

Image

Ideation Stage

Here I'll be sharing how the idea of this final Project came up......

You may read about my Final Project Wi-Ro, From this LINK

Checking Feasability

For checking feasability I went through different pages online which were relevant to my project like : I looked out for interfacing OLED with Atmega 328p / Arduino, I looked out for Mesh networking etc etc.

Here I am sharing links which are relevant to this project:

1. OLED Screen Interfacing

2. Mesh Networking

3. IR remote

4. Real Time clock (RTC)

5. LM35 Temperature sensor

6. Relay with Arduino

After going through all the pages, I came to a conclusion that yes.... it is possible to make this system.... All I have to do is integrate all of them together...... So I started finalising my requirements and move ahead with the procurement.

Procurement

BOM

Image

I was supposed to submit the BOM to my local Instructor so that the lab can arrange for all the materials...... The tools and equipments were too used from the lab.

Hardware and Software development

Hacking into IR Remote
The first thing I did was I tried to hack into an IR Remote and use its button values...... I wanted the button values to be broadcasted over the Network and only particular node recieves that ......

For doing this my apporach firstly was to decode the IR signals using using some microcontroller >> then send the data to NODE MCU and then the NODE MCU would be broadcasting that data..... well this time it was a fail..... what I did was I made the groud of the remote common with the Arduino I was using and then used Analog input of the arduino connected to the cathode of the IR LED of the remote.

Image Image

This was a normal remote and I used arduino IR Remote Library to decode the values of the buttons..... but I was unable to decode the values of this remote .......

Everytime the same button was having a different value

Download Code/Library for IR Remote testing

Then I doubted wether the remote is working or not ...... to test the remote I used my phones camera..... IR rays are visible to Cameras....

You may see in the video the LED is blinking which shows that the IR Trnsmitter is transmitting signals but I am unable to decode them.

Then I thought lets have a look over its IC and see what it is doing... but unfortunately ... I dont Know why there is nothing available over the internet about the IC this remote was using ...

Then I even thought lets use the multiplexed data lines...... I kinda reverse engineered the IC .... Here're my observations :

1. One pin must be Vcc.... this would be the pin which is connected to the +ive of the AA Battery...

2. One pin must be GND .... this would be the pin which is connected to the -ive of the AA Battery...

3. Only one pin is going to the Cathode of the IR Transmitter.... that means this must be the encoded output from the IC...

4. After these 3 pins identification there was one pin which was not connected to anything... So I just named it NC ( Not connected ).

5. Now there were 12 pins remaining..... I counted the number of buttons which came out to be 36..... It clicked ok these 12 lines are multiplexed data lines......

I thought lets replace this IC with 12 Input microcontroller and use that as button click identifier... but this approach was ruled out ..... as I wanted the Wifi thing within the remote and with any extra Circuit on the remote ...... it wont be possible.... So I came back to IR transmitter signal decoding

After this I tried to work with a new Remote.....

Image

I tried the same procedure with this remote also but again I was unable to decode the results ...... So now I changed my approach ..... I thought lets use IR Reciever and then decode the results..... I tried this and BAmmmmmm.... It worked ..... worked like a charm.... I was good to move ahead with the remote now.....

Image Image

Image Image

I taped the IR reciver near to Transmitter

Now the problem was I cannot put one arduino inside the remote .... along with Node MCU.... So I started trying to interface that IR reciver with Node MCU...... Fortunately I found a library which was almost similar to the arduino IR Library....

Now it was time to fit all the things inside the remote ..... I used dremel to cut some parts inside the remote to make room for the NODE MCU.... I also made one hole at the base of the remote, to pass the USB cable ( I didnt wanted to open the remote again and again to change the code + NODE MCU was not having any power source inside the remote )

Image Image

Even now the remote was not able to close.... So I decided to cut the NODE MCU Pins.... so that the height can be reduced ....

Image Image

Image Image

Now it was a complete fit ...... everything was inside the remote and it was closable ...

Download Code/Library for IR Remote + NMCU

Testing the network
Now that the remote was ready with the code.... It was time to realize the mesh network.....

A mesh network is a local network topology in which the infrastructure nodes (i.e. bridges, switches and other infrastructure devices) connect directly, dynamically and non-hierarchically to as many other nodes as possible and cooperate with one another to efficiently route data from/to clients. This lack of dependency on one node allows for every node to participate in the relay of information. Mesh networks dynamically self-organize and self-configure, which can reduce installation overhead. The ability to self-configure enables dynamic distribution of workloads, particularly in the event that a few nodes should fail. This in turn contributes to fault-tolerance and reduced maintenance costs.

Mesh topology may be contrasted with conventional star/tree local network topologies in which the bridges/switches are directly linked to only a small subset of other bridges/switches, and the links between these infrastructure neighbours are hierarchical. While star-and-tree topologies are very well established, highly standardized and vendor-neutral, vendors of mesh network devices have not yet all agreed on common standards, and interoperability between devices from different vendors is not yet assured.

~Source - Wikipedia

I first understood whether Node MCU was able to make a mesh or not ...... Fortunately it was possible to make Mesh network using NODE MCU.

I used Painless mesh to create MESH Network....

Intro to painlessMesh

PainlessMesh is a library that takes care of the particulars of creating a simple mesh network using esp8266 and esp32 hardware. The goal is to allow the programmer to work with a mesh network without having to worry about how the network is structured or managed.

True ad-hoc networking

PainlessMesh is a true ad-hoc network, meaning that no-planning, central controller, or router is required. Any system of 1 or more nodes will self-organize into fully functional mesh. The maximum size of the mesh is limited (we think) by the amount of memory in the heap that can be allocated to the sub-connections buffer and so should be really quite high.

JSON based

PainlessMesh uses JSON objects for all its messaging. There are a couple of reasons for this. First, it makes the code and the messages human readable and painless to understand and second, it makes it painless to integrate painlessMesh with javascript front-ends, web applications, and other apps. Some performance is lost, but I haven’t been running into performance issues yet. Converting to binary messaging would be fairly straight forward if someone wants to contribute.

Wifi & Networking

PainlessMesh is designed to be used with Arduino, but it does not use the Arduino WiFi libraries, as we were running into performance issues (primarily latency) with them. Rather the networking is all done using the native esp32 and esp8266 SDK libraries, which are available through the Arduino IDE. Hopefully though, which networking libraries are used won’t matter to most users much as you can just include painlessMesh.h, run the init() and then work the library through the API.

PainlessMesh is not IP networking

PainlessMesh does not create a TCP/IP network of nodes. Rather each of the nodes is uniquely identified by its 32bit chipId which is retrieved from the esp8266/esp32 using the system_get_chip_id() call in the SDK. Every node will have a unique number. Messages can either be broadcast to all of the nodes on the mesh, or sent specifically to an individual node which is identified by its `nodeId.

Limitations and caveats

Try to avoid using delay() in your code. To maintain the mesh we need to perform some tasks in the background. Using delay() will stop these tasks from happening and can cause the mesh to lose stability/fall apart. Instead we recommend using the scheduler included in painlessMesh. That scheduler is a slightly modified version of the TaskScheduler library. Documentation can be found here. For other examples on how to use the scheduler see the example folder. painlessMesh uses the sdk provided by esp8266/esp32. Please be aware that as a result painlessMesh is incompatible with using the WiFi.h wrappers provided by the vendor, because both painlessMesh and the WiFi.h try to bind to the same events (e.g. disconnect event). Instead if you want to use the WiFi chip you will need to use same sdk as painlessMesh. Try to be conservative in the number of messages (and especially broadcast messages) you sent per minute. This is to prevent the hardware from overloading. Both esp8266 and esp32 are limited in processing power/memory, making it easy to overload the mesh and destabilise it. And while painlessMesh tries to prevent this from happening, it is not always possible to do so. Messages can go missing or be dropped due to high traffic and you can not rely on all messages to be delivered. One suggestion to work around is to resend messages every so often. Even if some go missing, most should go through. Another option is to have your nodes send replies when they receive a message. The sending nodes can the resend the message if they haven’t gotten a reply in a certain amount of time.

~Source - Painless Mesh Documentation

Now I started with the painless mesh and tried to make a network between 2 nodes and check whether they are sharing the data or not..... Here is the testing video of the same ..... You may notice that the LEDs are blinking in sync... which means that the nodes are in sync and sharing data......

Each node broadcasts a message to all the node

From here I started thinking how can I uniquely send some data to a particular node...... So I Simply used IF condition and read the message which was being broadcasted... if it satisfies the condition then it'll accept the mesage otherwise it'll ignore the message.

Code


#include "painlessMesh.h"


#define   LED             2       // GPIO

#define   BLINK_PERIOD    3000
#define   BLINK_DURATION  100

#define   MESH_SSID       "Rahul"
#define   MESH_PASSWORD   "heyo1234"
#define   MESH_PORT       5555

painlessMesh  mesh;
bool calc_delay = false;
SimpleList nodes;
long int rahul;
void sendMessage() ; // Prototype
Task taskSendMessage( TASK_SECOND * 1, TASK_FOREVER, &sendMessage ); // start with a one second interval

// Task to blink the number of nodes
Task blinkNoNodes;
bool onFlag = false;

void setup() {
  Serial.begin(115200);

  pinMode(LED, OUTPUT);
  pinMode(D2, INPUT);
  pinMode(D1, INPUT);

  //mesh.setDebugMsgTypes( ERROR | MESH_STATUS | CONNECTION | SYNC | COMMUNICATION | GENERAL | MSG_TYPES | REMOTE ); // all types on
  //mesh.setDebugMsgTypes(ERROR | DEBUG | CONNECTION | COMMUNICATION);  // set before init() so that you can see startup messages
  mesh.setDebugMsgTypes(ERROR | DEBUG | CONNECTION);  // set before init() so that you can see startup messages

  mesh.init(MESH_SSID, MESH_PASSWORD, MESH_PORT);
  mesh.onReceive(&receivedCallback);
  mesh.onNewConnection(&newConnectionCallback);
  mesh.onChangedConnections(&changedConnectionCallback);
  mesh.onNodeTimeAdjusted(&nodeTimeAdjustedCallback);
  mesh.onNodeDelayReceived(&delayReceivedCallback);

  mesh.scheduler.addTask( taskSendMessage );
  taskSendMessage.enable() ;

  blinkNoNodes.set(BLINK_PERIOD, (mesh.getNodeList().size() + 1) * 2, []() {
      // If on, switch off, else switch on
      if (onFlag)
        onFlag = false;
      else
        onFlag = true;
      blinkNoNodes.delay(BLINK_DURATION);

      if (blinkNoNodes.isLastIteration()) {

        blinkNoNodes.setIterations((mesh.getNodeList().size() + 1) * 2);

        blinkNoNodes.enableDelayed(BLINK_PERIOD -
            (mesh.getNodeTime() % (BLINK_PERIOD*1000))/1000);
      }
  });
  mesh.scheduler.addTask(blinkNoNodes);
  blinkNoNodes.enable();

  randomSeed(analogRead(A0));
}

void loop() {
  mesh.update();
  digitalWrite(LED, !onFlag);

  }

void sendMessage() {
  String msg = "Hello from node ";
  msg += mesh.getNodeId();
  msg += " myFreeMemory: " + String(ESP.getFreeHeap());
  msg += " noTasks: " + String(mesh.scheduler.size());
  bool error = mesh.sendBroadcast(msg);

  if (calc_delay) {
    SimpleList::iterator node = nodes.begin();
    while (node != nodes.end()) {
      mesh.startDelayMeas(*node);
      node++;
    }
    calc_delay = false;
  }

  Serial.printf("Sending message: %s\n", msg.c_str());

  taskSendMessage.setInterval( random(TASK_SECOND * 1, TASK_SECOND * 5));  // between 1 and 5 seconds
}


void receivedCallback(uint32_t from, String & msg) {
  Serial.printf("startHere: Received from %u msg=%s\n", from, msg.c_str());
  String str = msg.c_str();
 // Serial.println(str[2]);
  if(str[2]=='l')
  {
  Serial.println("yes");
  digitalWrite(D1, HIGH);
  }
  else
  {
    Serial.println("no");
    digitalWrite(D1, LOW);
  }

}

void newConnectionCallback(uint32_t nodeId) {
  onFlag = false;
  blinkNoNodes.setIterations((mesh.getNodeList().size() + 1) * 2);
  blinkNoNodes.enableDelayed(BLINK_PERIOD - (mesh.getNodeTime() % (BLINK_PERIOD*1000))/1000);

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

void changedConnectionCallback() {
  Serial.printf("Changed connections %s\n", mesh.subConnectionJson().c_str());
  onFlag = false;
  blinkNoNodes.setIterations((mesh.getNodeList().size() + 1) * 2);
  blinkNoNodes.enableDelayed(BLINK_PERIOD - (mesh.getNodeTime() % (BLINK_PERIOD*1000))/1000);

  nodes = mesh.getNodeList();

  Serial.printf("Num nodes: %d\n", nodes.size());
  Serial.printf("Connection list:");

  SimpleList::iterator node = nodes.begin();
  while (node != nodes.end()) {
    Serial.printf(" %u", *node);
    node++;
  }
  Serial.println();
  calc_delay = true;
}

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

void delayReceivedCallback(uint32_t from, int32_t delay) {
  Serial.printf("Delay to node %u is %d us\n", from, delay);
} 

Here in the code .... Sending message and recieved call back functions are the one which I was intrested in ..... playing with these 2 functions satisfied my need .....

Furthur All the details can be read from the documentation page of the Library. I am attaching all the documentation related to this.... Here is the testing video of the same....

Image

Download Test Codes/Library for Mesh Network

Making PCB
Now that my basic elements of the project were tested ...... I decided to move ahead with the PCB..... I decided to make a board which would be having these features :

1. The board should be interfaced with the NODE MCU.

2. The Board should be able to connect with th AC Appliance via Screw terminals.

3. The Board should be able to drive relays for switching.

4. The Board should be able to drive one OLED Screen.

5. The Board should have some 5v and Gnd Headers, incase power supply is required by any module.

6. The board should have an interfacing with Temperature sensor.

7. The board should have an interfacing with RTC via I2C.

Now that all the requirements were listed ... it was clear ATTINY was no where in the scope of use..... I'll have to use 328P.... so I decided to modify satasha kit.... here are the schematics.

Image

Image

All the things in the Schematic might look familier except ULN 2003a, So here is a little description of this IC

The ULN2003A is an array of seven NPN Darlington transistors capable of 500 mA, 50 V output. It features common-cathode flyback diodes for switching inductive loads. It can come in PDIP, SOIC, SOP or TSSOP packaging. In the same family are ULN2002A, ULN2004A, as well as ULQ2003A and ULQ2004A, designed for different logic input levels.

Image

I used this IC to actuate the relays as the signal from the microcontroller is not strong enough to drive the relay itself..... This IC works as an current amplifier and provides enough current to actuate the relay.

Now here is the Board layout

Image

It may be noted in this board the track widths are different ...... Its because of the current rating... The tracks which are driving the AC Appliance are most tick ones..... Otherwise on the board minimun track width is about 16mils and Max is 100mils....whole PCB was manually routed

Image

Conversion of Board layout to PNG Image

This procedure has been documented in week 7 i.e Electronic Design week (LINK)

Conversion of PNG Image to .RML file

This procedure has been documented in week 5 i.e Electronic Production (LINK)

After Milling PCB

Image

Soldering

Image Image

After soldering all the components over the board .... I tried to program it .... I dumped the Blink Code.... It was working but I observed one issue with my board ..... the Blinking LED was hindered soon as I touched the board ..... I didnt knew why it was happening ......

Here is the video of that problem

I thought this was due to some surface currents.... and the board needs some filtering .... So I added a capacitor parallel to the power supply .... but there was no improvement ......... then I thought there is a lot of metal which is not being used on the board ...... it must be having some charge .... or my body also may be having some charge ....... So I need to discharge that ..... I added a capacitor between the GND and the surface ....... this was working like a discharge capacitor... the problem was solved .....

Here is the video of the board with solved problem

After this I cleaned the PCB with WD 40

Image Image

From the button below your can download all the things related to PCB..... Traces, Border, Layout, Schematic everything is in it.

Download PCB Files

Testing OLED with the Board
I used Adafruit Library to interface OLED with the Board

Image This was the schematic for interfacing OLED with arduino ..... the Pins I used on my Boards were 5,6,7 and 8.

Code

          #include "SPI.h"
          #include "Wire.h"
          #include "Adafruit_GFX.h"
          #include "Adafruit_SSD1306.h"

          // If using software SPI (the default case):
          #define OLED_MOSI   5
          #define OLED_CLK   6
          #define OLED_DC    7
          #define OLED_CS    9
          #define OLED_RESET 8
          Adafruit_SSD1306 display(OLED_MOSI, OLED_CLK, OLED_DC, OLED_RESET, OLED_CS);

          /* Uncomment this block to use hardware SPI
          #define OLED_DC     6
          #define OLED_CS     7
          #define OLED_RESET  8
          Adafruit_SSD1306 display(OLED_DC, OLED_RESET, OLED_CS);
          */

          #define NUMFLAKES 10
          #define XPOS 0
          #define YPOS 1
          #define DELTAY 2

          #define LOGO16_GLCD_HEIGHT 16
          #define LOGO16_GLCD_WIDTH  16
          static const unsigned char PROGMEM logo16_glcd_bmp[] =
          { B00000000, B11000000,
            B00000001, B11000000,
            B00000001, B11000000,
            B00000011, B11100000,
            B11110011, B11100000,
            B11111110, B11111000,
            B01111110, B11111111,
            B00110011, B10011111,
            B00011111, B11111100,
            B00001101, B01110000,
            B00011011, B10100000,
            B00111111, B11100000,
            B00111111, B11110000,
            B01111100, B11110000,
            B01110000, B01110000,
            B00000000, B00110000 };

          #if (SSD1306_LCDHEIGHT != 32)
          #error("Height incorrect, please fix Adafruit_SSD1306.h!");
          #endif

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

            // by default, we'll generate the high voltage from the 3.3v line internally! (neat!)
            display.begin(SSD1306_SWITCHCAPVCC);
            // init done

            // Show image buffer on the display hardware.
            // Since the buffer is intialized with an Adafruit splashscreen
            // internally, this will display the splashscreen.
            display.display();
            delay(2000);

            // Clear the buffer.
            display.clearDisplay();

            // draw a single pixel
            display.drawPixel(10, 10, WHITE);
            // Show the display buffer on the hardware.
            // NOTE: You _must_ call display after making any drawing commands
            // to make them visible on the display hardware!
            display.display();
            delay(2000);
            display.clearDisplay();

            // draw many lines
            testdrawline();
            display.display();
            delay(2000);
            display.clearDisplay();

            // draw rectangles
            testdrawrect();
            display.display();
            delay(2000);
            display.clearDisplay();

            // draw multiple rectangles
            testfillrect();
            display.display();
            delay(2000);
            display.clearDisplay();

            // draw mulitple circles
            testdrawcircle();
            display.display();
            delay(2000);
            display.clearDisplay();

            // draw a white circle, 10 pixel radius
            display.fillCircle(display.width()/2, display.height()/2, 10, WHITE);
            display.display();
            delay(2000);
            display.clearDisplay();

            testdrawroundrect();
            delay(2000);
            display.clearDisplay();

            testfillroundrect();
            delay(2000);
            display.clearDisplay();

            testdrawtriangle();
            delay(2000);
            display.clearDisplay();

            testfilltriangle();
            delay(2000);
            display.clearDisplay();

            // draw the first ~12 characters in the font
            testdrawchar();
            display.display();
            delay(2000);
            display.clearDisplay();

            // draw scrolling text
            testscrolltext();
            delay(2000);
            display.clearDisplay();

            // text display tests
            display.setTextSize(1);
            display.setTextColor(WHITE);
            display.setCursor(0,0);
            display.println("Hello, world!");
            display.setTextColor(BLACK, WHITE); // 'inverted' text
            display.println(3.141592);
            display.setTextSize(2);
            display.setTextColor(WHITE);
            display.print("0x"); display.println(0xDEADBEEF, HEX);
            display.display();
            delay(2000);
            display.clearDisplay();

            // miniature bitmap display
            display.drawBitmap(30, 16,  logo16_glcd_bmp, 16, 16, 1);
            display.display();

            // invert the display
            display.invertDisplay(true);
            delay(1000);
            display.invertDisplay(false);
            delay(1000);
            display.clearDisplay();

            // draw a bitmap icon and 'animate' movement
            testdrawbitmap(logo16_glcd_bmp, LOGO16_GLCD_HEIGHT, LOGO16_GLCD_WIDTH);
          }


          void loop() {

          }


          void testdrawbitmap(const uint8_t *bitmap, uint8_t w, uint8_t h) {
            uint8_t icons[NUMFLAKES][3];

            // initialize
            for (uint8_t f=0; f< NUMFLAKES; f++) {
              icons[f][XPOS] = random(display.width());
              icons[f][YPOS] = 0;
              icons[f][DELTAY] = random(5) + 1;

              Serial.print("x: ");
              Serial.print(icons[f][XPOS], DEC);
              Serial.print(" y: ");
              Serial.print(icons[f][YPOS], DEC);
              Serial.print(" dy: ");
              Serial.println(icons[f][DELTAY], DEC);
            }

            while (1) {
              // draw each icon
              for (uint8_t f=0; f< NUMFLAKES; f++) {
                display.drawBitmap(icons[f][XPOS], icons[f][YPOS], bitmap, w, h, WHITE);
              }
              display.display();
              delay(200);

              // then erase it + move it
              for (uint8_t f=0; f< NUMFLAKES; f++) {
                display.drawBitmap(icons[f][XPOS], icons[f][YPOS], bitmap, w, h, BLACK);
                // move it
                icons[f][YPOS] += icons[f][DELTAY];
                // if its gone, reinit
                if (icons[f][YPOS] > display.height()) {
                  icons[f][XPOS] = random(display.width());
                  icons[f][YPOS] = 0;
                  icons[f][DELTAY] = random(5) + 1;
                }
              }
             }
          }


          void testdrawchar(void) {
            display.setTextSize(1);
            display.setTextColor(WHITE);
            display.setCursor(0,0);

            for (uint8_t i=0; i < 168; i++) {
              if (i == '\n') continue;
              display.write(i);
              if ((i > 0) && (i % 21 == 0))
                display.println();
            }
            display.display();
          }

          void testdrawcircle(void) {
            for (int16_t i=0; i0; i-=5) {
              display.fillTriangle(display.width()/2, display.height()/2-i,
                               display.width()/2-i, display.height()/2+i,
                               display.width()/2+i, display.height()/2+i, WHITE);
              if (color == WHITE) color = BLACK;
              else color = WHITE;
              display.display();
            }
          }

          void testdrawroundrect(void) {
            for (int16_t i=0; i=0; i-=4) {
              display.drawLine(0, display.height()-1, display.width()-1, i, WHITE);
              display.display();
            }
            delay(250);

            display.clearDisplay();
            for (int16_t i=display.width()-1; i>=0; i-=4) {
              display.drawLine(display.width()-1, display.height()-1, i, 0, WHITE);
              display.display();
            }
            for (int16_t i=display.height()-1; i>=0; i-=4) {
              display.drawLine(display.width()-1, display.height()-1, 0, i, WHITE);
              display.display();
            }
            delay(250);

            display.clearDisplay();
            for (int16_t i=0; i<=display.height(); i+=4) {
              display.drawLine(display.width()-1, 0, 0, i, WHITE);
              display.display();
            }
            for (int16_t i=0; i<=display.width(); i+=4) {
              display.drawLine(display.width()-1, 0, i, display.height()-1, WHITE);
              display.display();
            }
            delay(250);
          }

          void testscrolltext(void) {
            display.setTextSize(2);
            display.setTextColor(WHITE);
            display.setCursor(10,0);
            display.clearDisplay();
            display.println("scroll");
            display.display();

            display.startscrollright(0x00, 0x0F);
            delay(2000);
            display.stopscroll();
            delay(1000);
            display.startscrollleft(0x00, 0x0F);
            delay(2000);
            display.stopscroll();
            delay(1000);
            display.startscrolldiagright(0x00, 0x07);
            delay(2000);
            display.startscrolldiagleft(0x00, 0x07);
            delay(2000);
            display.stopscroll();
          }
        
Test video

Download Code/Arduino Library

Temperature Sensor LM35 Test

Image Image

Code

 int val;
 int tempPin = 7;

 void setup()
 {
 Serial.begin(9600);
 }
 void loop()
 {
 val = analogRead(tempPin);
 float mv = ( val/1024.0)*5000;
 float cel = mv/10;
 float farh = (cel*9)/5 + 32;

 Serial.print("TEMPRATURE = ");
 Serial.print(cel);
 Serial.print("*C");
 Serial.println();
 delay(1000);
  }
Test Video

OLED + RTC + Temp Sensor test

Image

  #include "SPI.h"
  #include "Wire.h"
  #include "Adafruit_GFX.h"
  #include "Adafruit_SSD1306.h"
  #include "TimeLib.h"
  #include "DS1307RTC.h"
  //DS3231 rtc(SDA, SCL);
  #define OLED_MOSI 5
  #define OLED_CLK 6
  #define OLED_DC 7
  #define OLED_CS 9
  #define OLED_RESET 8
  Adafruit_SSD1306 display(OLED_MOSI, OLED_CLK, OLED_DC, OLED_RESET, OLED_CS);
  const int sensor_pin = A7;
  float temp;
  float output;
  float ra;
  void setup()
  {
      Serial.begin(9600);

  pinMode(sensor_pin,INPUT);
  display.begin(SSD1306_SWITCHCAPVCC);
  Serial.println("hello");
  //rtc.setDOW(WEDNESDAY);     // Set Day-of-Week to SUNDAY
  //rtc.setTime(19, 02, 0);     // Set the time to 12:00:00 (24hr format)
  //rtc.setDate(7, 12, 2017);   // Set the date to June 6th, 2017
  display.clearDisplay();
  display.setTextColor(WHITE);
  display.setTextSize(2);
  display.setCursor(0,0);
  display.print("   Wi-Ro ");
  display.setCursor(0,17);
  display.print("  by Rahul");
  display.display();
  delay(5000);
  }

  void loop()
  {
      tmElements_t tm;

  output = analogRead(sensor_pin);
  ra = ( output/1024.0)*5000;
  temp = ra/10;
    if (RTC.read(tm)) {

  display.clearDisplay();
  display.setTextSize(2);
  display.setCursor(20,0);
  display.print(tm.Hour);
      display.print(':');
     display.print(tm.Minute);
      display.print(':');
     display.print(tm.Second);
  display.setTextSize(1);
  display.setCursor(0,15);
  display.print(tm.Day);
      display.print('/');
      display.print(tm.Month);
      display.print('/');
      display.print(tmYearToCalendar(tm.Year));
      display.setTextSize(1);
  display.setCursor(70,15);
    int r = dow(tmYearToCalendar(tm.Year),tm.Month,tm.Day);
   switch(r)
    {
      case 1 :
      display.print("Tuesday");
      break;
      case 2 :
      display.print("Wednesday");
      break;
      case 3:
      display.print("Thursday");
      break;
      case 4:
      display.print("Friday");
      break;
      case 5:
      display.println("Saturday");
      break;
      case 6:
      display.print("Sunday");
      break;
      case 7:
      display.print("Monday");
      break;

      default:
      display.print("hello");
      }
  //display.print("Wednesday");
  display.setTextSize(1);
  display.setCursor(20,25);
  display.print("Temp: ");
  display.print(temp);
  display.print("C");
  display.display();
    }
  delay(1000);
  }



  int leap (int year)
  {
    return year*365 + (year/4) - (year/100) + (year/400);
  }


  int zeller (int year, int month, int day)
  {
    year += ((month+9)/12) - 1;
    month = (month+9) % 12;
    return leap (year) + month*30 + ((6*month+5)/10) + day + 1;
  }

  int dow (int year, int month, int day)
  {
    return (zeller (year, month, day) % 7) + 1;
  } 

Download Code/Arduino Library

Electrical Panel
Now that the board and all the related things were tested I moved ahead with making the electric panel

Firstly I tried to maek the panel using mica sheet and laser cut it ...... but Laser was not able to cut it ...... So I decided to use Dremel Saw......

Image Image

Although this worked but I was not satisfied with the quality of the Board... I went ahead and decided to Laser cut the panel....

Laser Cutting

This procedure has been documented in Week 4 i.e Computer Controlled Cutting(LINK)

Image Image

Download Panel DXF

Soon as the Panel was ready I mounted switches and screen on the Panel....

Image

3D Frame for OLED Screen
3D printing

This procedure has been documented in Week 6 i.e 3D Scanning & Printing(LINK)

Image

I 3D printed frame for the OLED which is shown above..... Here I am attaching Solid works file and Maker bot file for the same.

Download Solidwork files

Panel Wiring
After my panel was ready I moved towards wiring the panel.... I wired up my board and the the switches..... the switches are connected in series to the load ...... and the relays are connected in parallel to the switches.....

Hero Shot

Image

Image Image

After the panel was wired..... I thought lets close the back side of the panel also ..... So, I designed the back panel..... One part of this panel would be 3D printed and the another one would be laser cutted....

Image Image

Image Image

Download Backpanel files

Embedded Codes

Now that everything is ready ...... its time to make the codes for the setup....

For a bigger picture...... here is how the controllers are talking to each other.....

There are total 3 Microcontrollers in the whole system.....

1.The first one is in the Remote ( Node MCU ), its job is to decode the IR signal and broadcast that value over the mesh network.

2.The second one is on the panel ( Node MCU ), its job is to recieve the broadcasted signal and see if its for this panel or not ..... If the value is for the same panel then it'll change the state of some pins from HIGH to LOW.

3. The Third one is the Atmega 328P, its job is to monitor temperature, display control, check date and time, check if the NODE MCU has recieved anything or not..... if it has recived something then it has to act accordingly and switch the relays ....

Remote Code


      #ifndef UNIT_TEST
      #include "Arduino.h"
      #endif
      #include "IRremoteESP8266.h"
      #include "IRrecv.h"
      #include "IRutils.h"

      #include "painlessMesh.h"
        String msg = "000000";


      #define   LED             2       // GPIO

      #define   BLINK_PERIOD    3000
      #define   BLINK_DURATION  100

      #define   MESH_SSID       "Rahul"
      #define   MESH_PASSWORD   "heyo1234"
      #define   MESH_PORT       5555

      uint16_t RECV_PIN = 14;

      IRrecv irrecv(RECV_PIN);
      int atr;
      decode_results results;

      painlessMesh  mesh;
      bool calc_delay = false;
      SimpleList nodes;
      long int rahul;
      void sendMessage() ; // Prototype
      Task taskSendMessage( TASK_SECOND * 1, TASK_FOREVER, &sendMessage ); // start with a one second interval

      // Task to blink the number of nodes
      Task blinkNoNodes;
      bool onFlag = false;

      void setup() {
        Serial.begin(115200);
        //mesh.setDebugMsgTypes( ERROR | MESH_STATUS | CONNECTION | SYNC | COMMUNICATION | GENERAL | MSG_TYPES | REMOTE ); // all types on
        //mesh.setDebugMsgTypes(ERROR | DEBUG | CONNECTION | COMMUNICATION);  // set before init() so that you can see startup messages
        mesh.setDebugMsgTypes(ERROR | DEBUG | CONNECTION);  // set before init() so that you can see startup messages

        mesh.init(MESH_SSID, MESH_PASSWORD, MESH_PORT);
        mesh.onReceive(&receivedCallback);
        mesh.onNewConnection(&newConnectionCallback);
        mesh.onChangedConnections(&changedConnectionCallback);
        mesh.onNodeTimeAdjusted(&nodeTimeAdjustedCallback);
        mesh.onNodeDelayReceived(&delayReceivedCallback);

        mesh.scheduler.addTask( taskSendMessage );
        taskSendMessage.enable() ;

        blinkNoNodes.set(BLINK_PERIOD, (mesh.getNodeList().size() + 1) * 2, []() {
            // If on, switch off, else switch on
            if (onFlag)
              onFlag = false;
            else
              onFlag = true;
            blinkNoNodes.delay(BLINK_DURATION);

            if (blinkNoNodes.isLastIteration()) {

              blinkNoNodes.setIterations((mesh.getNodeList().size() + 1) * 2);

              blinkNoNodes.enableDelayed(BLINK_PERIOD -
                  (mesh.getNodeTime() % (BLINK_PERIOD*1000))/1000);
            }
        });
        mesh.scheduler.addTask(blinkNoNodes);
        blinkNoNodes.enable();

        randomSeed(analogRead(A0));
          irrecv.enableIRIn();  // Start the receiver

      }

      void loop() {
        mesh.update();
        digitalWrite(LED, !onFlag);
        if (irrecv.decode(&results)) {
          // print() & println() can't handle printing long longs. (uint64_t)
            atr= (results.value);
            msg= String(atr);
            irrecv.resume();
            delay(10);

        }
      }

      void sendMessage() {

        bool error = mesh.sendBroadcast(msg);

        if (calc_delay) {
          SimpleList::iterator node = nodes.begin();
          while (node != nodes.end()) {
            mesh.startDelayMeas(*node);
            node++;
          }
          calc_delay = false;
        }

        Serial.printf("Sending message: %s\n", msg.c_str());

        taskSendMessage.setInterval( random(TASK_SECOND * 1, TASK_SECOND * 5));  // between 1 and 5 seconds
      }


      void receivedCallback(uint32_t from, String & msg) {
        Serial.printf("startHere: Received from %u msg=%s\n", from, msg.c_str());
        String str = msg.c_str();
       // Serial.println(str[2]);
        if(str[2]=='l')
        {
        Serial.println("yes");
       // digitalWrite(D1, HIGH);
        }
        else
        {
          Serial.println("no");
         // digitalWrite(D1, LOW);
        }

      }

      void newConnectionCallback(uint32_t nodeId) {
        onFlag = false;
        blinkNoNodes.setIterations((mesh.getNodeList().size() + 1) * 2);
        blinkNoNodes.enableDelayed(BLINK_PERIOD - (mesh.getNodeTime() % (BLINK_PERIOD*1000))/1000);

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

      void changedConnectionCallback() {
        Serial.printf("Changed connections %s\n", mesh.subConnectionJson().c_str());
        onFlag = false;
        blinkNoNodes.setIterations((mesh.getNodeList().size() + 1) * 2);
        blinkNoNodes.enableDelayed(BLINK_PERIOD - (mesh.getNodeTime() % (BLINK_PERIOD*1000))/1000);

        nodes = mesh.getNodeList();

        Serial.printf("Num nodes: %d\n", nodes.size());
        Serial.printf("Connection list:");

        SimpleList::iterator node = nodes.begin();
        while (node != nodes.end()) {
          Serial.printf(" %u", *node);
          node++;
        }
        Serial.println();
        calc_delay = true;
      }

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

      void delayReceivedCallback(uint32_t from, int32_t delay) {
        Serial.printf("Delay to node %u is %d us\n", from, delay);
      }
    

Node MCU Code @ Panel


        #include "painlessMesh.h"


        #define   LED             2       // GPIO

        #define   BLINK_PERIOD    3000
        #define   BLINK_DURATION  100

        #define   MESH_SSID       "Rahul"
        #define   MESH_PASSWORD   "heyo1234"
        #define   MESH_PORT       5555

        painlessMesh  mesh;
        bool calc_delay = false;
        SimpleList nodes;
        long int rahul;
        void sendMessage() ; // Prototype
        Task taskSendMessage( TASK_SECOND * 1, TASK_FOREVER, &sendMessage ); // start with a one second interval
        String hello ;
        // Task to blink the number of nodes
        Task blinkNoNodes;
        bool onFlag = false;
        //String str ="123" ;
        void setup() {
          Serial.begin(115200);

          pinMode(LED, OUTPUT);
          pinMode(D7, OUTPUT);
          pinMode(D6, OUTPUT);

          //mesh.setDebugMsgTypes( ERROR | MESH_STATUS | CONNECTION | SYNC | COMMUNICATION | GENERAL | MSG_TYPES | REMOTE ); // all types on
          //mesh.setDebugMsgTypes(ERROR | DEBUG | CONNECTION | COMMUNICATION);  // set before init() so that you can see startup messages
          mesh.setDebugMsgTypes(ERROR | DEBUG | CONNECTION);  // set before init() so that you can see startup messages

          mesh.init(MESH_SSID, MESH_PASSWORD, MESH_PORT);
          mesh.onReceive(&receivedCallback);
          mesh.onNewConnection(&newConnectionCallback);
          mesh.onChangedConnections(&changedConnectionCallback);
          mesh.onNodeTimeAdjusted(&nodeTimeAdjustedCallback);
          mesh.onNodeDelayReceived(&delayReceivedCallback);

          mesh.scheduler.addTask( taskSendMessage );
          taskSendMessage.enable() ;

          blinkNoNodes.set(BLINK_PERIOD, (mesh.getNodeList().size() + 1) * 2, []() {
              // If on, switch off, else switch on
              if (onFlag)
                onFlag = false;
              else
                onFlag = true;
              blinkNoNodes.delay(BLINK_DURATION);

              if (blinkNoNodes.isLastIteration()) {

                blinkNoNodes.setIterations((mesh.getNodeList().size() + 1) * 2);

                blinkNoNodes.enableDelayed(BLINK_PERIOD -
                    (mesh.getNodeTime() % (BLINK_PERIOD*1000))/1000);
              }
          });
          mesh.scheduler.addTask(blinkNoNodes);
          blinkNoNodes.enable();

          randomSeed(analogRead(A0));
        }

        void loop() {
          mesh.update();
          digitalWrite(LED, !onFlag);
          Serial.println(hello);
          if(hello=="876330")
          {
            digitalWrite(D6, HIGH);
            //Serial.println("d5 LOW ");
          }
          else if (hello=="847665")
          {
            digitalWrite(D6,LOW);
            //Serial.println("d5 HIGH");
          }
          else if (hello=="843570")
          {
            digitalWrite(D7,HIGH);
            //Serial.println("d6 LOW");
          }
          else if (hello=="839475")
          {
            digitalWrite(D7,LOW);
            //Serial.println("d6 HIGH");
          }
          delay(500);

          }

        void sendMessage() {


          if (calc_delay) {
            SimpleList::iterator node = nodes.begin();
            while (node != nodes.end()) {
              mesh.startDelayMeas(*node);
              node++;
            }
            calc_delay = false;
          }

          //Serial.printf("Sending message: %s\n", msg.c_str());

          taskSendMessage.setInterval( random(TASK_SECOND * 1, TASK_SECOND * 5));  // between 1 and 5 seconds
        }


        void receivedCallback(uint32_t from, String & msg) {
          Serial.printf("startHere: Received from %u msg=%s\n", from, msg.c_str());
        String  str = msg.c_str();
        hello=str;
         // Serial.println(str[2]);
          if(str=="876330")
          {
          Serial.println("yes");
          //digitalWrite(D1, HIGH);
          }
          else
          {
            Serial.println("no");
           // digitalWrite(D1, LOW);
          }

        }

        void newConnectionCallback(uint32_t nodeId) {
          onFlag = false;
          blinkNoNodes.setIterations((mesh.getNodeList().size() + 1) * 2);
          blinkNoNodes.enableDelayed(BLINK_PERIOD - (mesh.getNodeTime() % (BLINK_PERIOD*1000))/1000);

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

        void changedConnectionCallback() {
          Serial.printf("Changed connections %s\n", mesh.subConnectionJson().c_str());
          onFlag = false;
          blinkNoNodes.setIterations((mesh.getNodeList().size() + 1) * 2);
          blinkNoNodes.enableDelayed(BLINK_PERIOD - (mesh.getNodeTime() % (BLINK_PERIOD*1000))/1000);

          nodes = mesh.getNodeList();

          Serial.printf("Num nodes: %d\n", nodes.size());
          Serial.printf("Connection list:");

          SimpleList::iterator node = nodes.begin();
          while (node != nodes.end()) {
            Serial.printf(" %u", *node);
            node++;
          }
          Serial.println();
          calc_delay = true;
        }

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

        void delayReceivedCallback(uint32_t from, int32_t delay) {
          Serial.printf("Delay to node %u is %d us\n", from, delay);
        } 

ATMEGA 328P Code

    #include "SPI.h"
    #include "Wire.h"
    #include "Adafruit_GFX.h"
    #include "Adafruit_SSD1306.h"
    #include "TimeLib.h"
    #include "DS1307RTC.h"
    #define OLED_MOSI 5
    #define OLED_CLK 6
    #define OLED_DC 7
    #define OLED_CS 9
    #define OLED_RESET 8
    Adafruit_SSD1306 display(OLED_MOSI, OLED_CLK, OLED_DC, OLED_RESET, OLED_CS);
    const int sensor_pin = A7;
    float temp;
    float output;
    float ra;


    void setup()
    {
              Serial.begin(9600);
              pinMode(2,INPUT);
              pinMode(3,INPUT);
              pinMode(14,OUTPUT);
              pinMode(15,OUTPUT);
              pinMode(sensor_pin,INPUT);
              display.begin(SSD1306_SWITCHCAPVCC);
              display.clearDisplay();
              display.setTextColor(WHITE);
              display.setTextSize(2);
              display.setCursor(0,0);
              display.print("   Wi-Ro ");
              display.setCursor(0,17);
              display.print("  by Rahul");
              display.display();
              delay(5000);
    }



    void loop()
    {
        tmElements_t tm;

    output = analogRead(sensor_pin);
    ra = ( output/1024.0)*5000;
    temp = ra/10;
    if (digitalRead(3)== HIGH)
              {
                digitalWrite(14,HIGH);

              }
    if (digitalRead(3)==LOW)
              {
                digitalWrite(14,LOW);
              }
     if (digitalRead(2)==HIGH)
              {
                digitalWrite(15,HIGH);
              }
     if (digitalRead(2)==LOW)
              {
                digitalWrite(15,LOW);
              }

    if (RTC.read(tm))
      {

          display.clearDisplay();
          display.setTextSize(2);
          display.setCursor(20,0);
          display.print(tm.Hour);
          display.print(':');
          display.print(tm.Minute);
          display.print(':');
          display.print(tm.Second);
                      display.setTextSize(1);
                      display.setCursor(0,15);
                      display.print(tm.Day);
                      display.print('/');
                      display.print(tm.Month);
                      display.print('/');
                      display.print(tmYearToCalendar(tm.Year));
                                display.setTextSize(1);
                                display.setCursor(70,15);
                                  int r = dow(tmYearToCalendar(tm.Year),tm.Month,tm.Day);
                                 switch(r)
                                  {
                                    case 1 :
                                    display.print("Tuesday");
                                    break;
                                    case 2 :
                                    display.print("Wednesday");
                                    break;
                                    case 3:
                                    display.print("Thursday");
                                    break;
                                    case 4:
                                    display.print("Friday");
                                    break;
                                    case 5:
                                    display.println("Saturday");
                                    break;
                                    case 6:
                                    display.print("Sunday");
                                    break;
                                    case 7:
                                    display.print("Monday");
                                    break;

                                    default:
                                    display.print("hello");
                                    }

                                                  display.setTextSize(1);
                                                  display.setCursor(20,25);
                                                  display.print("Temp: ");
                                                  display.print(temp);
                                                  display.print("C");
                                                  display.display();
                                                    }


    delay(200);
    }



    int leap (int year)
              {
                return year*365 + (year/4) - (year/100) + (year/400);
              }


    int zeller (int year, int month, int day)
              {
                year += ((month+9)/12) - 1;
                month = (month+9) % 12;
                return leap (year) + month*30 + ((6*month+5)/10) + day + 1;
              }

    int dow (int year, int month, int day)
              {
                return (zeller (year, month, day) % 7) + 1;
              }
            

Download Main Codes

Test in Real Conditions

I wired up the setup with the Lamps which were in lab.... and tested the whole system from different locations within adn outside the lab.

Image

Final Video

Conclusion from testing

1. The whole setup was working perfect.

2. No part(s) were found heating or shorting.

3. No abnormal behavior was observed during the trial run.

4. A delay was noticed between the remote and the NODE, may be thats due to network.

Future Scope

1. The whole system can be hosted on a dedicated Server and this flexible connectivity can be utlized Via Internet or May be through a Mobile App.

2. These panels can be integrated with the Electricity Meters and the data can be sent to Electricity Board. Cutomers can even have a consumption Breakup of their Bills.

3. Recharable Remote.

4. Button configration using a User Login window.

5. Managing Appliances with some software on PC.

6. Mobile app for the system.

7. Integration with other systems at home, E.g: Cameras, Climate control etc.

Innovation is not the product of logical thought

Even though the final product is tied to a logical structure.

~Albert Einstein

Contact Me

I am happy to talk you through any projects or run live demos to see how they look like.

Top