Arduino MKR1000 IoT Bundle Kit - Review

Table of contents

RoadTest: Arduino MKR1000 IoT Bundle Kit

Author: gordonmx

Creation date:

Evaluation Type: Development Boards & Tools

Did you receive all parts the manufacturer stated would be included in the package?: True

What other parts do you consider comparable to this product?: A number of Maker related dev boards

What were the biggest problems encountered?: Poor documentation, buggy code, poor support turnaround. If it were not for tutorial #4 & #5 I would have rated this kit with 8's & 9's for demo software and ease of use (i.e.47/60).

Detailed Review:

First off, I would like to thank Arduino and Element 14 for the opportunity to evaluate the Arduino MKR1000 IoT Bundle Kit (“the Kit”). 

 

Summary Conclusion –

 

NOTE:  Before I start my review, as many of you already know, Arduino has been part of the Maker world almost from the beginning.  Can anyone tell me what the name means?  See the end of the “Summary Conclusion” for the answer. 

 

As mentioned in the body of my review, the Arduino MKR1000 IoT Bundle Kit is a sold an introduction to the world of the Internet of Things (IoT) programming.  There are many physical methods to communicate between devices, such as WiFi, BLE, infrared, hardwire, etc., with the first 2 mentioned being the most popular current ones.  For the “Kit”, only WiFi is used.  The physical kit makes up the hardware portion of the learning process and relies on a set of 5 tutorials from Arduino’s project hub assembled by a team at Arduino to complete the package.  Each tutorial is segmented in sub-labs or circuits that demonstrations a different part of the overall lesson’s goal.  The last step of the tutorial combines the individual sub-circuits into the final project.  Although all the tutorials are authored by “Team Arduino” it quickly becomes apparent that not all tutorials exhibit the same quality of detail and documentation. This was certainly true of tutorials #4 and #5 , “The Nerd” and “Plant Communicator”.  For more details, see the write ups for each tutorial in the body of my review.  On the positive side, all the tutorials have their own comment section so that any errors that are found can be post there for future programmers.  In some cases, the documentation/code had been update after comments had been posted. 

 

The kit comes with more than enough components to complete the tutorials, but some parts needed to be “modified” to reliably build and test some circuits.  The organization of the parts included with the kit could be better since, although packaged by type, the component would end up all mixed together in the plastic box supplied.  A simple list of parts is included on the kit’s information page found at the Arduino’s website, but another helpful improvement would be to list the manufacturer and their part number(s) so that the programmer could find their data sheet (& pinout).  

 

The integrated development environment (IDE) includes both a desktop and online version.  Both versions are straight forward and easy to use, but lack the full functionality and visibility found in most manufacturer-specific compilers. But as already pointed out, the target market for most Arduino development is at the introduction programming level.  NOTE: Due the unavailability of some of the required “include” files not all the tutorials could be built using the online compiler.

 

As noted in the body of my review, the MKR1000 does have issues connecting with some Windows 7 based PCs through the USB port and may require an intermediate USB2 hub.

 

After you get past the short comings of the documentation and the limitations of the development environment, the product works very well for its target market.  You may be able to locate the individual components at lower price, but for the convenience of getting everything in one package the $73.90 US price is reasonable. 

 

Pros –

  • All the components are included to complete the recommended tutorials (& more).
  • Easy to use free IDE includes both desktop and web versions.
  • Powerful 32bit MCU.
  • Full WiFi functionality onboard the MKR1000.
  • Open source hardware and software.
  • Easy to access user community forum for seeking help.

 

Cons –

  • No “getting started” documentation or web link was included with the kit.  Not even a link on the box.  You had to go to the Arduino site and find the information from the store page.
  • The tutorials were very light weight, targeted for the beginning programmer.
  • Some of the components supplied did not fit well in the proto board and had to be modified.
  • The code supplied had no error checking for debug purpose.
  • The tutorial documentation was not always correct and could use a general cleaned up.

 

Wish List –

  • Links to additional resource such as manuals, books, etc. would be useful.
  • Optional labs with deeper content, such as security.
  • For what online documentation is available, a pdf print option, like on most wiki pages, would be useful.
  • Better component organizer (i.e. multi-compartment, etc.).

 

Overall the design is workable and I would recommend the Arduino MKR1000 IoT Bundle Kit if you would like an introduction to Arduino IoT concepts and design, but more could have been done with the concept.

 

Answer to today’s question:  Arduino is Italian for “strong one”.  It is an Italian masculine first name.  It also is the name of the bar where the founders of the platform would gather.  As a side note, there has been a fair amount of fighting over you really came up with the original Arduino concept and design.  Was it the student or his advisers?  So in 2015, after some legal battles, Arduino became the name in the United States, where Genuino became the name outside the United States.  As to thumb its nose at Arduino, Genuino is Spanish for authentic or legitimate.

 

Useful Documents Links-

NOTE:  Logging in to the respective websites may be required to access some of the documents.

 

Element 14 RoadTest Website:

    https://www.element14.com/community/roadTests/2068/l/arduino-mkr1000-iot-bundle-kit

Arduino MKR1000 IoT Bundle Product Website:  https://store.arduino.cc/usa/arduino-iot-mkr1000-bundle

Arduino Desktop IDE Software:  https://www.arduino.cc/en/Main/Software

Arduino Language Reference Website: https://www.arduino.cc/reference/en/

Arduino WiFi101 Library Reference Website:  https://www.arduino.cc/en/Reference/WiFi101

Arduino IoT Cloud Website:  https://www.arduino.cc/en/IoT/HomePage

Arduino MKR1000 WiFi Product Website: https://store.arduino.cc/usa/arduino-mkr1000

Instructables Website:  https://www.instructables.com/community/What-does-Arduino-mean/

Wikipedia: https://en.wikipedia.org/wiki/Arduino

History of Arduino (Core Electronics): https://core-electronics.com.au/tutorials/history-of-arduino.html

Arduino MKR1000 Getting Started (Hackster.io): 

    https://www.hackster.io/charifmahmoudi/arduino-mkr1000-getting-started-08bb4a

IoT-Arduino-Cookbook (marcoschwartz - GitHub):  https://github.com/marcoschwartz/iot-arduino-cookbook

IoT w/ Arduino Cookbook (NaveenKJ / Packt Publishing – GitHub)

    https://github.com/PacktPublishing/Internet-of-Things-with-Arduino-Cookbook

Getting Started with the Arduino/Genuino MKR1000:  https://www.arduino.cc/en/Guide/MKR1000

Arduino Web-based IoT Cloud – Getting Started Tutorial Project:

    https://create.arduino.cc/projecthub/133030/iot-cloud-getting-started-c93255

Arduino MKR1000 IoT Training Videos by Augmented Reality Center:

    https://www.youtube.com/watch?v=CZN1jBrmy3g

    https://www.youtube.com/watch?v=Oh7vM0t6vGI0

    https://www.youtube.com/watch?v=ShPphIYBXF0

    https://www.youtube.com/watch?v=Qjligd_TSFI

    https://www.youtube.com/watch?v=WGmIIu9aP_w

    https://www.youtube.com/watch?v=_pn3jBHZ-5I

Arduino MKR1000 IoT Bundle Project Hub Website: https://create.arduino.cc/projecthub?by=part&part_id=54617

Arduino MKR1000 IoT Bundle Tutorial#1 Website:

    https://create.arduino.cc/projecthub/arduino/love-you-pillow-f08931?ref=platform&ref_id=424_trending___&offset=3

 

Part I – A Little History…

Before digging into details of the “Kit”, let’s start with a little history.  Arduino is an open-source hardware and software platform licensed under the GNU Lesser General Public License (LGPL) or the GNU General Public License (GPL) allowing anyone abetting by these licenses to freely build and distribution the platform.  Still the evolution of the platform has not been without some legal challenges. 

 

The Arduino project was started in 2003 by a graduate student, Hernando Barragan, at the Interaction Design Institute Ivrea (IDII) in Italy as part of his Master’s thesis.  He was trying to create a low cost alternative to the BASIC Stamp microcontroller boards used by many software students at the time.  In 2003 the low cost dev boards that we are use too today didn’t exist.  To add to the problem, before the days of Eclipse-based IDEs, most microcontroller manufacturers charged for their development software. 

 

Initially the project was named “Wiring”.  The hardware portion of “Wiring” consisted of a PCB driven by an 8-bit high-performance low-power Atmel AVR RISC-based microcontroller with 16KBs of memory, the ATmega128.  Other microcontroller were considered, including Microchip’s PIC family, but Hernando settled on Atmel based partly on the availability of their open source tools for compiling, linking and uploading code. 

 

For the project’s software tools, Hernando adapted a current development platform, “Processing” (must be something about the “ing”), that had been co-created in part by his thesis supervisors, Massimo Banzi and Casey Reas.

 

Since most of the programming students were not engineers, the C-based coding interface/syntax had to be simplified.  Even the coding was labeled “sketches” instead of programs to soften the progress for non-technical folks.  Instead of having to define IO ports and/or memory locations using physical addressing an abstraction layer was added to hide the details.  Programmers could define an IO port with a simple port number and mode.  No bitwise masking was needed.  Of course this did limit the flexibility of the platform, but the project was more of an introduction to the world of microcontrollers.  This is still a major draw today for many hobbyists and first time programmers. 

 

image

 

Part II – The Unpacking:  8^) with a little 8^(

Welcome to the first part of many of my evaluation of the Arduino MKR1000 IoT Bundle Kit, part number GKX00006.  I have read many different views on the value and/or importance of the unpacking segment, but I feel it is important because it is often our first glimpse into what to expect in the product and support.

 

I’m excited to road test the “Kit” because I help students in our local high school and I wanted to see how this might fit into that work.  I was also interested in learning a little more about Atmel’s IoT solutions.

 

The shipping box arrived undamaged, always a good sign, and well packed.  Obviously, it wasn’t shipped by Amazon 8^).

imageimageimageimage

      image

Of course the heart of the kit is the MKR1000 -

imageimageimage

imageimage

One of my first tasks was to sort the parts into a multi- compartment container for easier access.

image

Part III – Connecting and Powering Up the MKR1000:

At this time I normally power up the kit to get a feel for whether the unit will show any sign of life.  The ON and CHARGE LEDs both turned on, but it appeared no program examples were preloaded.  Still no battery is installed, the charge LED turned off after a few moments. 

 

Using the device manager, I checked if my PC recognized the MKR1000, but found nothing.  So to the documentation (and Google) to discover that not all Windows 7 USB3 ports work with the board.  One workaround on the Arduino forum suggested inserting a USB2 hub between the board and my USB3 PC port.  Not all hubs work, but after the 3rd one, my PC found the board.

       imageimage

The good news is it did power up (i.e. no smoke).  The next step would be to install the IDE, then try programming the board. 

 

Part IV - Software Installation:

There are two versions of the IDE that can be used to compile and upload programs to the MKR1000. 

 

Arduino Web Editor -

The first is the newer cloud-based Arduino Web Editor (Creator).  You must register and login to their website to use the editor, but no additional installation is needed.  NOTE: The Web Editor does not support Internet Explorer.

   image

The Web Editor is free to use, sort of.  One of the short comings of the Web Editor is the limit on how many programs you can build and upload, 100.  You can upgrade for a monthly fee ($6.99/month US) to 250 programs. Fortunately, for the tutorials in this review the limitations of the free version are not a problem.

image  

You do have to configure the cloud-based IDE by adding your board, account information and the required header files.  Click on “Getting Started” to walk you through the process.

     imageimage

imageimage

   imageimage

         imageimage

If you get an error during configuring you editor, a help menu with appear like the one for the USB port or connecting to the Cloud.

      imageimage

The rest of the configuration menu is used to verify the setup was successful. 

image

To monitor and troubleshoot your program, the serial console maybe opened by clicking the monitor label on the left side of the IDE.

  image

One additional issue I have found using the online IDE was the limited available header files to include in your project.  For one of the tutorial I had to use the desktop IDE because the required header files were not available for the Web Editor.

 

Arduino Desktop IDE (D-IDE)-

The current stable version of the Desktop IDE is v1.8.10 and supports Windows (XP or later), Mac OS X (10.8 or later) and Linux.  For using with the kit, the installation is straight forward using all the default settings.  You may be prompted to install the USB driver, click “install”.  Note: You can click on the “detail” button to watch the progress and when the install is complete press “close”.

  imageimage

Aside from a slightly different look, the D-IDE uses many of the same tools as the web version, which can be accessed from the “Tools” drop down tab.

  image

With the D-IDE, you define your working directory. 

   

Part IV –Lab Tutorials:

Now for some fun stuff.  As mentioned earlier the MKR IoT Bundle web site includes a link to 5 experiments to familiarize the programmer with using the MKR1000 in IoT applications.  The experiments were written my Team Arduino and pretty much follow the same format. Each tutorial is broken up into individual segments that when combined represents a completed project.  For links to each respective tutorial see the “Useful Document Links” section of this review.

 

The instructions for each tutorial, although all were created by Team Arduino, vary in the amount of informational details.  Almost all the projects covered very little information about how the WiFi connection worked. 

 

NOTE:  I initially loaded the latest versions of each library, but had errors during compiling. The errors clearly indicated I needed an earlier version of a library.  After reloading the library with the earlier version, the program compiled successfully.

 

NOTE:  Running the program often required the serial debug monitor feature of the IDE, but the compiler would error out if the monitor was running when using the Desktop IDE. This was not a problem with the online compiler.

 

NOTE:  As with other tutorial in the kit, a template is included for the cardboard box the electronics goes in.  I considered the boxes to be optional and did not assemble them.

 

There are a couple of general issues that are minor, but could be used to improve the useful of the kit.  First, the documentation for the kit bundle has a list of components included, but it would be more useful to include the actual manufacture part number (or equivalent).  For example, the photo transistor has no spec or pinout diagram. The layout draws are not always clear as to which pin is the emitter and which is the collector.

 

Second, I like the approach where the labs are partitioned into functional areas so that each segment can be verified before combine them in to the finally project, but I have found a number of errors in the individual code segment.  For more details, see individual tutorial comments.

 

Third, some of the component’s leads (i.e. potentiometer, push button, buzzer, etc.) do not fit well in the proto board.  Some parts would back out of the holes in the board make the circuit intermittent. To fix the problem, I had to solder wires to the leads.

image

Ha, I said the website lists 5 experiments for the MKR1000, but if you follow the link there is actual 6 projects. Ok, there are only 5 projects, but 1 useful “IoT Cloud – Getting Started” tutorial as well.  Ok Ok there is 1 more useful tutorial if you take the time to look, “Getting started with the Arduino/Genuino MKR1000”.  The latter displays links to other lab tutorials not included in this review.  Both additional tutorials are recommended.

 

Pre-Tutorial #.1 - Getting started with the Arduino/Genuino MKR1000

This tutorial goes over the different IDE options for compiling, uploading and debugging your MKR1000.  It discussions common issues, such as USB drivers, port settings, library installation and power supply precautions. Included is your 1st project to blink an LED.  Very useful.

 

NOTE:  I initially had problems connecting to the dev board through my USB port.  After researching the issue on the community forum, it turns out the MKR1000 has trouble connecting to USB3 ports on Windows 7 PCs requiring an external USB2 hub be inserted between the board and the PC’s USB3 port.

 

Pre-Tutorial #.2 - IoT Cloud – Getting Started

Another great tutorial to help understand the MKR1000 connectivity to the “cloud” or more accurately the Arduino IoT Cloud.  In the process of completing the lab, you create an object in the cloud that is then used to control an LED (surprise, surprise) through the MKR1000.  Other components are added, including a pushbutton switch, which leads to an example of using a debouncing library.

 

Tutorial #1 – I Love You Pillow

This tutorial takes a whimsical approach at creating a pillow that when hugged generates simulated heartbeat using a small buzzer, as well as sends a message via the WiFi interface to a messaging app, Telegram Bot.  The latter feature demonstrates the IoT connectivity of the MKR1000 while the former feature only offer some audible feedback that the program is running not requiring any WiFi connection.  The IoT connect for this lab is only one way from the board to the messaging app.

 

The lab is broken up 4 parts: 1) Introduction and creation of the Telegram Bot, 2) Setup of a capacitive sensor using the MKR1000, 3) Heartbeat audio generation and finally 4) combining the segments to create the “I Love You” pillow.

 

The main emphasis of this tutorial is the introduction, creation of and connects to the Telegram Bot using the MKR1000, so most of the time is spent here.  The first part of the lab segment only covers the intro and creation of the bot and does not require the MKR1000.  The MKR1000 will be integrated in during segment 4.   The format for creating a bot is tricky.  The main bot is the @BotFather, under which you generate your own bot workspace then give it a username (username must end with the word “Bot”).  At the end of each new bot creation, an API token is generated to communicate with your bot.

imageimage

Step 1:  Look for @BotFather                                                      Step 2: Type “/newbot” to create a new Telegram bot

    imageimage

Step 3:  Type your Bot’s name                                                    Step 4:  Type your Bot’s username

 

Finally, an API token is generated to connect with your Telegram Bot.

   image

  Step 5: Note down your Bot’s API token

 

A full list of Telegram commands is shown below and can be display using the /help command:

 

    /newbot - create a new bot

    /mybots - edit your bots [beta]

    /help - list commands

 

    Edit Bots

    /setname - change a bot's name

    /setdescription - change bot description

    /setabouttext - change bot about info

    /setuserpic - change bot profile photo

    /setcommands - change the list of commands

    /deletebot - delete a bot

 

    Bot Settings

    /token - generate authorization token

    /revoke - revoke bot access token

    /setinline - toggle inline mode (https://core.telegram.org/bots/inline)

    /setinlinegeo - toggle inline location requests (https://core.telegram.org/bots/inline#location-based-results)

    /setinlinefeedback - change inline feedback (https://core.telegram.org/bots/inline#collecting-feedback) settings

    /setjoingroups - can your bot be added to groups?

    /setprivacy - toggle privacy mode (https://core.telegram.org/bots#privacy-mode) in groups

 

    Games

    /mygames - edit your games (https://core.telegram.org/bots/games) [beta]

    /newgame - create a new game (https://core.telegram.org/bots/games)

    /listgames - get a list of your games

    /editgame - edit a game

    /deletegame - delete an existing game

 

The rest of the lab uses the MKR1000 and either the Arduino web editor (shown in the tutorial) or the downloadable desktop IDE.  Either requires the following libraries list in the documentation:  WiFi101, Arduinojson and CapacitiveSensor.  The process for loading the libraries is outlined through a link in the tutorial and very simple and straight forward.

 

Although the tutorial would supply additional program information through the use of #ProTips in the documentation, I wish more information was made available for how the project worked (i.e. theory of operation. etc.)

 

The final code for the “I Love You” pillow is shown below:

 

#include <WiFi101.h>

#include <SPI.h> 
#include <TelegramBot.h> 
#include <Adafruit_SleepyDog.h>  
// Initialize WiFi connection to the router 
char ssid[] = "xxxx";             // your network SSID (name) 
char pass[] = "yyyy";           // your network key 
// Initialize Telegram BOT 
const char BotToken[] = "xxxx"; 
WiFiSSLClient client; 
TelegramBot bot (BotToken, client); 
 
void setup() { 
 Serial.begin(115200); 
 while (!Serial) {} 
 delay(3000); 
 // attempt to connect to WiFi network: 
 Serial.print("Connecting WiFi: "); 
 Serial.println(ssid); 
 while (WiFi.begin(ssid, pass) != WL_CONNECTED) { 
   Serial.print("."); 
   delay(500); 
 } 
 Serial.println(""); 
 Serial.println("WiFi connected"); 
 bot.begin(); 
 Watchdog.enable(10000); // set the timer to 10 sec 
} 
 
void loop() { 
   Watchdog.reset(); // if this function is not called within 10 seconds the board will
                     // reset itself 
   message m = bot.getUpdates(); // Read new messages 
   if ( m.chat_id != 0 ){ // Checks if there are some updates 
     Serial.println(m.text); 
     bot.sendMessage(m.chat_id, m.text);  // Reply to the same chat with the same text 
   } else { 
     Serial.println("no new message"); 
   } 
} 

image

   imageimage

The result of the IoT connection to the Telegram Bot.

image

     

Tutorial #2 – PuzzleBox

This tutorial creates a locked box that is opened by setting 3 potentiometers to the correct combination. The locking mechanism is servo motor that rotates in either the locked or unlocked position.  The project also uses an LED for visual feedback and, when the correct combination is dialed in, a buzzer is used to play a tune from Star Wars. The program communicates to a cellphone through the Android app, Blynk.  Blynk offers 2 way communications between the phone and the MKR1000, but for the main program is only used to send a new combination to the MKR1000. 

 

Like the first tutorial, the lab is broken up into individual segments (6 parts) that when combined completes the entire project.  As with the last project this is helpful for reducing the amount of troubleshooting since you can check out each sub-circuit before continuing. The sub-sections included 1) the introduction and creation of the Blynk, 2) setup of an LCD display, 3) The addition of 3 potentiometers, 4) a RGB LED, 5) a buzzer, 6) a servo motor and finally 7) combining the segments to create the complete project.

 

The main emphasis of this tutorial is the introduction, creation of and connects to the Blynk app using the MKR1000, so it is discussed first.  After the initial setup, the IoT connect does not enter into the picture until the buzzer segment is tested.

 

During the program the Blynk connection is setup,

 

  Blynk.begin(auth, ssid, password); // start Blynk functionalities

 

Then the app is accessed by running,

 

Blynk.run(); // poll new combination values from the online app

 

An issue that was a little puzzling (no pun intended) at first was the lack of error checking when the WiFi did not connect.  I had typed in a letter incorrectly for my SSID.  Nothing happened and there was no error message, so I thought I had wired the board wrong. I checked and double checked my wiring and everything was fine, it just too long to discover the problem.

 

Another issue I discovered during the buzzer segment and carries over to the final project.  The tone() command used in the beep() subroutine is immediately followed by the noTone() command.  One of the tone() parameters defines the tone duration, but the command does not block the execution of other command until the duration is complete, as soon as the command is issued, the noTone()is executed.  This causes no sound to be produced.  The solution is to place a delay, as shown below, between the 2 commands.

 

void beep(int note, int duration)

{

//Play tone on buzzerPin

tone(buzzerPin, note, duration);

//Stop tone on buzzerPin

delay(duration); // <<<<< Added line

noTone(buzzerPin);

delay(50);

//Increment counter

counter++;

}

The final code for the “PuzzleBox” tutorial is shown below:

 

2_Puzzle_Box.ino

 

#include <LiquidCrystal.h>

#include <SPI.h>

#include <WiFi101.h>

#include <BlynkSimpleWiFiShield101.h>

#include <Servo.h>

 

#define buzzerPin 1

#include "Melody.h"

 

// RGB LED pins

int redPin = 6;

int greenPin = 8;

int bluePin = 7;

 

const char* ssid = SECRET_SSID;    //  your network SSID (name)

const char* password = SECRET_PSWD;  // your network password

char auth[] = SECRET_TOKEN; // your Blynk API token

 

// LCD screen pins

const int rs = 12,

          en = 11,

          d4 = 2,

          d5 = 3,

          d6 = 4,

          d7 = 5;

 

bool start = true;

 

// Variables to store the combination value

// Set the intitial combination to ( 1 1 1 )

int SliderValueOne = 1;

int SliderValueTwo = 1;

int SliderValueThree = 1;

 

int pos = 0;    // variable to store the servo position

Servo myservo;  // create servo object to control a servo

 

 

// Blynk functions to retrive values

BLYNK_WRITE(V1) {

  SliderValueOne = param.asInt(); // assigning incoming value from pin V1 to a variable

 

}

BLYNK_WRITE(V2) {

  SliderValueTwo = param.asInt(); // assigning incoming value from pin V1 to a variable

 

}

BLYNK_WRITE(V3) {

  SliderValueThree = param.asInt(); // assigning incoming value from pin V1 to a variable

 

}

 

LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

 

void setup() {

  pinMode(redPin, OUTPUT);

  pinMode(greenPin, OUTPUT);

  pinMode(bluePin, OUTPUT); 

  pinMode(buzzerPin, OUTPUT);

   

  analogWrite(A3, 0); // set the brightness of the LCD screen to the maximum value

  Serial.begin(9600);

  lcd.begin(16, 2); // begin LCD screen with 16 columns and 2 rows

  Blynk.begin(auth, ssid, password); // start Blynk functionalities

  myservo.attach(9);  // attaches the servo on pin 9 to the servo object

  myservo.write(pos); // set the servo in position 0

}

 

void loop() {

  // Variambles to temporarily store the combination

  int Temp_Slider_One_value = SliderValueOne;

  int Temp_Slider_Two_value = SliderValueTwo;

  int Temp_Slider_Three_value = SliderValueThree;

   

  Blynk.run(); // poll new combination values from the online app

   

  // check if combination values are changed and print them on the console

  if(Temp_Slider_One_value != SliderValueOne || Temp_Slider_Two_value != SliderValueTwo || Temp_Slider_Three_value != SliderValueThree){

    Serial.print("New combination: ");

    Serial.print(SliderValueOne);

    Serial.print(" ");

    Serial.print(SliderValueTwo);

    Serial.print(" ");

    Serial.println(SliderValueThree);

  }

   

  int PotOne = map(analogRead(A0), 100, 1023, 0, 9);

  int PotTwo = map(analogRead(A1), 100, 1023, 0, 9);

  int PotThree = map(analogRead(A2), 100, 1023, 0, 9);

  lcd.setCursor(0, 0);

  lcd.print(PotOne);

  lcd.setCursor(2, 0);

  lcd.print(PotTwo);

  lcd.setCursor(4, 0);

  lcd.print(PotThree);

 

 

  if (start) {

    giveColorFeedback(PotOne, PotTwo, PotThree);

    if (PotOne == SliderValueOne && PotTwo == SliderValueTwo && PotThree ==

    SliderValueThree) {

      play_jingle();

      open_the_box();

      blinkGreenLed();

      start = false;

    }

  }

   

  if(!start) {

    if(PotOne == 0 && PotTwo == 0 && PotThree == 0){

      close_the_box();

      start = true;

    }

  }

 

}

 

// Give feedback based on how close the potentiometer are to the combination value

// The more it's close the warmer is the color of the LED

void giveColorFeedback(int PotOne, int PotTwo, int PotThree) {

 

  if (abs(PotOne - SliderValueOne) <= 1 && abs(PotTwo - SliderValueTwo) <= 1 && abs(PotThree - SliderValueThree) <= 1 ) {

    // Red

    setColor(255, 0, 0);

  }

  else if (abs(PotOne - SliderValueOne) <= 3 && abs(PotTwo - SliderValueTwo) <= 3 && abs(PotThree - SliderValueThree) <= 3 ) {

    // yellow

    setColor(255, 255, 0);

  }

    else if (abs(PotOne - SliderValueOne) <= 4 && abs(PotTwo - SliderValueTwo) <= 4 && abs(PotThree - SliderValueThree) <= 4 ) {

    // aqua

    setColor(0, 255, 255); 

  }

  else {

    // blue

    setColor(0, 0, 255);

  }

}

 

void blinkGreenLed() {

  for (int a = 0; a < 2; a++) {

    for (int b = 0; b <= 255; b += 5) {

      setColor(0, b, 0);

      delay(5);

    }

    for (int b = 255; b >= 0; b -= 5) {

      setColor(0, b, 0);

      delay(5);

    }

  }

  for (int b = 0; b <= 255; b += 5) {

    setColor(0, b, 0);

    delay(5);

  }

}

 

// Send RGB values to the LED pins

void setColor(int red, int green, int blue){

  analogWrite(redPin, red);

  analogWrite(greenPin, green);

  analogWrite(bluePin, blue); 

}

 

void open_the_box(){

      for (pos = 0; pos <= 90; pos += 1) { // goes from 0 degrees to 90 degrees

myservo.write(pos); // tell servo to go to position in variable 'pos'

      delay(15);                       // waits 15ms for the servo to reach the position

    }

}

 

void close_the_box(){

    for (pos = 90; pos >= 0; pos -= 1) { // goes from 90 degrees to 0 degrees

      myservo.write(pos);              // tell servo to go to position in variable 'pos'

      delay(15);                       // waits 15ms for the servo to reach the position

    }

}

 

  1. Melody.h

 

const int c = 261;

const int d = 294;

const int e = 329;

const int f = 349;

const int g = 391;

const int gS = 415;

const int a = 440;

const int aS = 455;

const int b = 466;

const int cH = 523;

const int cSH = 554;

const int dH = 587;

const int dSH = 622;

const int eH = 659;

const int fH = 698;

const int fSH = 740;

const int gH = 784;

const int gSH = 830;

const int aH = 880;

 

const int ledPin1 = 8;

const int ledPin2 = 9;

 

int counter = 0;

 

void beep(int note, int duration)

{

  //Play tone on buzzerPin

  tone(buzzerPin, note, duration);

 

  //Play different LED depending on value of 'counter'

  if(counter % 2 == 0)

  {

    digitalWrite(ledPin1, HIGH);

    delay(duration);

    digitalWrite(ledPin1, LOW);

  }else

  {

    digitalWrite(ledPin2, HIGH);

    delay(duration);

    digitalWrite(ledPin2, LOW);

  }

 

  //Stop tone on buzzerPin

  noTone(buzzerPin);

 

  delay(50);

 

  //Increment counter

  counter++;

}

 

void play_jingle()

{

  beep(a, 500);

  beep(a, 500);   

  beep(a, 500);

  beep(f, 350);

  beep(cH, 150); 

  beep(a, 500);

  beep(f, 350);

  beep(cH, 150);

  beep(a, 650);

 

  delay(500);

 

  beep(eH, 500);

  beep(eH, 500);

  beep(eH, 500); 

  beep(fH, 350);

  beep(cH, 150);

  beep(gS, 500);

  beep(f, 350);

  beep(cH, 150);

  beep(a, 650);

 

  delay(500);

}

imageimage

       

The result of the IoT connection to the Blynk app.

imageimage

       

Tutorial #3 – Pavlov’s Cat

This tutorial uses a photo transistor to sense the presence of a cat to release food to the feeding tray while playing tune on a buzzer.  The size of the portion is controlled by a menu displayed on a Telegram bot app (see tutorial #1).  An added feature is a web server hosted through the MKR1000 access point mode function, as descripted in the tutorial’s #ProTip, to display a food dispenser report on a web page.

 

Like previous tutorials, the project is divided into smaller segments including the 1) photo transistor sensing, 2) playing a song with the buzzer, 3) opening the dispenser using a servo motor and finally 4) combining the segments into the completed project both with and without the web server functionality.

 

Unlike tutorial #1, with the use of the Telegram menu feature, the communication between the app and the hardware becomes 2-way.

 

During the program the Telegram connections is setup using,

 

#include <TelegramBot.h>

WiFiSSLClient telegram_client;

TelegramBot bot (BotToken, telegram_client);

TelegramKeyboard keyboard_one;

while (WiFi.begin(ssid, password) != WL_CONNECTED) {

 

Then the app is started by running,

 

  1. bot.begin();

 

NOTE:  See the full listings below for actual inter-communication commands between the MKR1000 and web/app.

 

I must say that I’m learning to appreciate the Arduino Team’s added contribution to my education by once again supplying “buggy” code in their tutorials to help me with my debugging skills. (or at least I’m beginning to expect buggy code - 8^)).  As an example, the layout diagram shows the servo motor, buzzer and photo transistor connected to pins 6, 7, and A0, respectively, but the code shows something different:

 

int buzzerPin = 6; // Pin attached to the buzzer

  1. myservo.attach(7);  // attaches the servo on pin 7 to the servo object

int value = analogRead(A6);

 

The final code for the “Pavlov’s Cat” tutorial is shown below:

 

3_Pavlov_s_Cat.ino

 

// ArduinoJson - Version: 5.13.4

#include <ArduinoJson.h>

#include <ArduinoJson.hpp>

#include <WiFi101.h>

#include <SPI.h>

#include <TelegramBot.h>

#include <Servo.h>

#include "pitches.h"

#include "html.h"

 

const char* ssid = SECRET_SSID;    //  your network SSID (name)

const char* password = SECRET_PSWD;  // your network password

const char BotToken[] = SECRET_BOT_TOKEN;

 

WiFiSSLClient telegram_client;

TelegramBot bot (BotToken, telegram_client);

TelegramKeyboard keyboard_one;

 

int PortionAmount = 1; // Set default amount of food to 1 portion

 

Servo myservo;  // create servo object to control a servo

int pos = 0;    // variable to store the servo position

 

int buzzerPin = 7; // Pin 7 attached to the buzzer

bool startDetecting = false;

unsigned long timer = 0;

String OldChatId = "";

 

WiFiServer server(80);

bool web_server_mode=false;

bool food=false;

bool cat_detected=false;

 

void setup() {

  Serial.begin(115200);

  delay(5000);

 

  // attempt to connect to WiFi network:

  Serial.print("Connecting WiFi: ");

  Serial.println(ssid);

  while (WiFi.begin(ssid, password) != WL_CONNECTED) {

    Serial.print(".");

    delay(500);

  }

  Serial.println("");

  Serial.println("WiFi connected");

 

  // choose the emoji you like using UNICODE

  // here's the list https://unicode.org/emoji/charts/full-emoji-list.html

 

  const char* MusicAndFood = "\U0001F3B6 +   \U0001F36A"; // Music + Cookie

  const char* MusicNoFood = "\U0001F3B6 NO   \U0001F36A"; // Music NO Cookie

  const char* OnePortion = "\U0001F408"; // CAT

  const char* TwoPortion = "\U0001F408 \U0001F408"; // 2 CATS

  const char* ThreePortion = "\U0001F408 \U0001F408 \U0001F408"; // 3 CATS

 

  // define your row's

  const char* row_one[] = {MusicAndFood, MusicNoFood};

  const char* row_two[] = {OnePortion, TwoPortion, ThreePortion};

  keyboard_one.addRow(row_one, 2); // assign a row to one or more keyboards

  keyboard_one.addRow(row_two, 3); // second argument is the length of the row

  bot.begin();

  myservo.attach(6);  // attaches the servo on pin 7 to the servo object

  pinMode(buzzerPin, OUTPUT);

}

 

void loop() {

 

if(!web_server_mode){

  message m = bot.getUpdates(); // Read new messages

  if ( m.chat_id != 0 ) { // Checks if there are some updates

    OldChatId = m.chat_id;

    Serial.println(m.text);

 

    if (m.text == "ud83cudfb6   + ud83cudf6a") { // if Melody AND food

      bot.sendMessage(m.chat_id, "Dispensing "+String(PortionAmount)+" portion of food right Now", keyboard_one);  // Reply to the same chat with a text and a custom keyboard

      playMelody(melodyOne, noteDurationsOne, 8);

      moveServo();

      startDetecting = true;

      food=true;

      timer = millis();

    }

    else if (m.text == "ud83cudfb6   NO ud83cudf6a") { // if Melody and NO food

      bot.sendMessage(m.chat_id, "At your command", keyboard_one);

      playMelody(melodyTwo, noteDurationsTwo, 11);

      startDetecting = true;

      food=false;

      timer = millis();

    }

    else if (m.text == "ud83dudc08") {

      PortionAmount = 1;

      bot.sendMessage(m.chat_id, "Food portion changed to 1", keyboard_one);

    }

    else if (m.text == "ud83dudc08 ud83dudc08") {

      PortionAmount = 2;

      bot.sendMessage(m.chat_id, "Food portion changed to 2", keyboard_one);

    }

    else if (m.text == "ud83dudc08 ud83dudc08 ud83dudc08") {

      PortionAmount = 3;

      bot.sendMessage(m.chat_id, "Food portion changed to 3", keyboard_one);

    }

    else if(m.text == "Server"){

      IPAddress ip = WiFi.localIP();

      web_server_mode=true;

      Serial.println(ip);

      String message = "To see the webpage go to http://"+IpToString(ip);

      Serial.println(message);

      bot.sendMessage(m.chat_id, message , keyboard_one);

      telegram_client.stop();

      delay(1000);

      server.begin();                           // start the web server on port 80

    }

    else bot.sendMessage(m.chat_id, "Hello !", keyboard_one);

  }

 

  if (startDetecting) {

    int value = analogRead(A0);

    Serial.println(value);

    if (value < 200) {

      String TimeValue = String((millis() - timer) / 1000);

      bot.sendMessage(OldChatId, "Cat detected! \nTime to reach the feeder: " + TimeValue + " seconds", keyboard_one);

      startDetecting = false;

      AddTableCell(food, "Yes", TimeValue);

       

    }

    else if (millis() - timer > 120000) {

      bot.sendMessage(OldChatId, "No cat detected in the past two minutes", keyboard_one);

      startDetecting = false;

      AddTableCell(food, "No", "/");

    }

  }

}

   

  if(web_server_mode){

     WiFiClient client = server.available();   // listen for incoming clients

 

  if (client) {                             // if you get a client,

    String currentLine = "";      // make a String to hold incoming data from the client

    while (client.connected()) {            // loop while the client's connected

      if (client.available()) {             // if there's bytes to read from the client,

        char c = client.read();             // read a byte, then

        if (c == '\n') {                    // if the byte is a newline character

          // if the current line is blank, you got two newline characters in a row.

          // that's the end of the client HTTP request, so send a response:

          if (currentLine.length() == 0) {

            // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)

            // and a content-type so the client knows what's coming, then a blank line:

            client.println("HTTP/1.1 200 OK");

client.println("Content-type:text/html");

            client.println();

            // the content of the HTTP response follows the header:

            client.print(html);

            // The HTTP response ends with another blank line:

            client.println();

            // break out of the while loop:

            break;

          }

          else {      // if you got a newline, then clear currentLine:

            currentLine = "";

          }

        }

else if (c != '\r') { // if you got anything else but a carriage return character,

          currentLine += c;      // add it to the end of the currentLine

        }

        // Check to see if the client pressed the button

        if (currentLine.endsWith("GET /BACKBUTTON")) {

          Serial.println("");

          Serial.println("back to telegram mode");

          Serial.println("");

          web_server_mode=false;

          client.stop();

          delay(1000);

          bot.begin();

          bot.sendMessage(OldChatId, "Back online !", keyboard_one);

        }

      }

    }

    // close the connection:

    client.stop();

  }

  }

}

 

void moveServo() {

  Serial.print("Moving servo ... ");

  for (pos = 0; pos <= 90; pos += 1) { // goes from 0 degrees to 90 degrees

    myservo.write(pos);              // tell servo to go to position in variable 'pos'

    delay(15);                     // waits 15ms for the servo to reach the position

 

  }

  delay(PortionAmount * 300);  // keep the box open for a time interval based on the amount of food you want to deliver

  for (pos = 90; pos >= 0; pos -= 1) { // goes from 90 degrees to 0 degrees

    myservo.write(pos);              // tell servo to go to position in variable 'pos'

    delay(15);                     // waits 15ms for the servo to reach the position

  }

  Serial.println("Exiting.");

}

 

void playMelody(int melody[], int noteDurations[], int numberOfNotes ) {

  Serial.print("Playing melody ... ");

 

  for (int thisNote = 0; thisNote < numberOfNotes; thisNote++) {

 

    // to calculate the note duration, take one second divided by the note type.

    //e.g. quarter note = 1000 / 4, eighth note = 1000/8, etc.

    int noteDuration = 1000 / noteDurations[thisNote];

    tone(buzzerPin, melody[thisNote], noteDuration);

 

    // to distinguish the notes, set a minimum time between them.

    // the note's duration + 30% seems to work well:

    int pauseBetweenNotes = noteDuration * 1.30;

    delay(pauseBetweenNotes);

    // stop the tone playing:

    noTone(buzzerPin);

  }

  Serial.println("Exiting!");

}

 

String IpToString(IPAddress& _address)

{

    String str = String(_address[0]);

    str += ".";

    str += String(_address[1]);

    str += ".";

    str += String(_address[2]);

    str += ".";

    str += String(_address[3]);

    return str;

}

 

  1. pitches.h

 

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

* Public Constants

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

 

#define NOTE_B0  31

#define NOTE_C1  33

#define NOTE_CS1 35

#define NOTE_D1  37

#define NOTE_DS1 39

#define NOTE_E1  41

#define NOTE_F1  44

#define NOTE_FS1 46

#define NOTE_G1  49

#define NOTE_GS1 52

#define NOTE_A1  55

#define NOTE_AS1 58

#define NOTE_B1  62

#define NOTE_C2  65

#define NOTE_CS2 69

#define NOTE_D2  73

#define NOTE_DS2 78

#define NOTE_E2  82

#define NOTE_F2  87

#define NOTE_FS2 93

#define NOTE_G2  98

#define NOTE_GS2 104

#define NOTE_A2  110

#define NOTE_AS2 117

#define NOTE_B2  123

#define NOTE_C3  131

#define NOTE_CS3 139

#define NOTE_D3  147

#define NOTE_DS3 156

#define NOTE_E3  165

#define NOTE_F3  175

#define NOTE_FS3 185

#define NOTE_G3  196

#define NOTE_GS3 208

#define NOTE_A3  220

#define NOTE_AS3 233

#define NOTE_B3  247

#define NOTE_C4  262

#define NOTE_CS4 277

#define NOTE_D4  294

#define NOTE_DS4 311

#define NOTE_E4  330

#define NOTE_F4  349

#define NOTE_FS4 370

#define NOTE_G4  392

#define NOTE_GS4 415

#define NOTE_A4  440

#define NOTE_AS4 466

#define NOTE_B4  494

#define NOTE_C5  523

#define NOTE_CS5 554

#define NOTE_D5  587

#define NOTE_DS5 622

#define NOTE_E5  659

#define NOTE_F5  698

#define NOTE_FS5 740

#define NOTE_G5  784

#define NOTE_GS5 831

#define NOTE_A5  880

#define NOTE_AS5 932

#define NOTE_B5  988

#define NOTE_C6  1047

#define NOTE_CS6 1109

#define NOTE_D6  1175

#define NOTE_DS6 1245

#define NOTE_E6  1319

#define NOTE_F6  1397

#define NOTE_FS6 1480

#define NOTE_G6  1568

#define NOTE_GS6 1661

#define NOTE_A6  1760

#define NOTE_AS6 1865

#define NOTE_B6  1976

#define NOTE_C7  2093

#define NOTE_CS7 2217

#define NOTE_D7  2349

#define NOTE_DS7 2489

#define NOTE_E7  2637

#define NOTE_F7  2794

#define NOTE_FS7 2960

#define NOTE_G7  3136

#define NOTE_GS7 3322

#define NOTE_A7  3520

#define NOTE_AS7 3729

#define NOTE_B7  3951

#define NOTE_C8  4186

#define NOTE_CS8 4435

#define NOTE_D8  4699

#define NOTE_DS8 4978

 

// notes in the melody:

int melodyOne[] = {

  NOTE_C4, NOTE_G3, NOTE_G3, NOTE_A3, NOTE_G3, 0, NOTE_B3, NOTE_C4

};

 

// note durations: 4 = quarter note, 8 = eighth note, etc.:

int noteDurationsOne[] = {

  4, 8, 8, 4, 4, 4, 4, 4

};

 

int melodyTwo[] = {

  NOTE_C5, NOTE_C5, NOTE_A4, NOTE_C5, 0, NOTE_G4, NOTE_C5, NOTE_F5, NOTE_E5, NOTE_C5, NOTE_C5

};

 

// note durations: 4 = quarter note, 8 = eighth note, etc.:

int noteDurationsTwo[] = {

  2, 2, 4, 4, 8, 4, 4, 4, 4, 4 , 4

};

 

  1. html.h (for web page)

 

String html_1 ="<style> body { background-color: #ECF1F1; min-height: 100%; font-family: monospace; letter-spacing: 2px; } table, th, td { border: 1px solid #ECF1F1; text-align: center; border-collapse: collapse; margin: auto; background-color: #F7F9F9; padding: 1%; margin-top: 50px; } table{ width: 70%; } .title { text-align: center; color: #0CA1A6; font-size: 1.5em; padding-top: 50px; } .bttn { padding-bottom: 1%; padding-top: 1%; margin-left: 35%; margin-right: 35%; margin-top: 10%; background-color: #00979D; text-align: center; color: #F7F9F9; font-size: 1.4em; } .bttn:hover { background-color: #008184; } a { text-decoration: none; }</style><body> <div class=\"title\">Food dispenser report</div> <table> <tr> <th>Food/no Food</th> <th>Cat detected</th> <th>Time to reach the food</th> </tr>";

String table_cells;

String html_2 ="</table> <a href=\"/BACKBUTTON\"><div class=\"bttn\">Disconect</div></a></body>";

String html;

 

void AddTableCell(bool food, String detected, String time){

  String cell="";

  if(food){

cell="<tr><td>Food</td>";

  }else{

    cell="<tr><td>No Food</td>";

  }

cell+="<td>"+detected+"</td>";

  cell+= "<td>"+ time + " s</td></tr>";

 

  table_cells+=cell;

  html = html_1 + table_cells + html_2;

}

 

      imageimage

 

Telegram Screenshot (without & with server mode) -

     imageimage

Webpage report -

  image

 

Tutorial #4 – The Nerd

This tutorial had great potential and was my greatest disappointment.  It’s goals were to cover the managing of the MKR1000 full WiFi functionality, using the flash memory to store data, using low power mode and the real time clock, but falls short due to very poor and in some cases incorrect documentation.  Did I learn something? Yes, but this lab is not for a beginner learning to use the MKR1000.  Like the other tutorials it is segmented to cover each goal individually, and then combine them together at the end.  Unless the material has changed a lot in the last 2 years, I don’t see how the sole reviewer could have “ready liked this…” out of the box. 

 

First the layout diagram does not come close to the actual code.  It doesn’t show the buzzer or battery connections.  The analog pin assignment is incorrect.  Second, there are many coding errors that keep the project from continuing pass the setup stage. 

 

NOTE:  Although in itself this issue is not a “real” issue, but for all the tutorials up to this point I have been able to compile and upload the code using both the online and the desktop IDEs.  All the include files for this project is not available for the online compiler, so the desktop IDE must be used.  This is not in the documentation.

 

The final code for the “The Nerd” tutorial is shown below:

 

4_The_Nerd.ino

 

#include "arduino_secrets.h"

#include <SPI.h>

#include <WiFi101.h>

#include <FlashStorage.h>

#include <RTCZero.h>

#include <WiFiUdp.h>

#include "ArduinoLowPower.h"

 

WiFiUDP udp;

WiFiUDP Udp;

RTCZero rtc;

 

#define MAGIC_NUMBER 0x7423  // arbitrary number to double check the validity of SSID

#define MaxNet 30  // max amount of network to be saved

 

// RGB LED pins

int redPin = 6;

int greenPin = 8;

int bluePin = 7;

 

const char* home_ssid = SECRET_SSID;    //  your network SSID (name)

const char* password = SECRET_PSWD;  // your network password

 

int BuzzerPin = 9;

int SensorPin = A2;

 

int PosToBeSaved = 0; // Variable used to navigate the array of saved networks

int daily_amount_of_food = 12; // The amount of food per day needed to survive

//int sleeping_time = 1800000; // 30 min *60 sec *1000 millisec

int sleeping_time = 60000; // 30 min *60 sec *1000 millisec

 

bool atHome = false;

bool hungry=true;

bool justWokeUp=false;

 

// Struct of variable to be saved in flash memory

typedef struct { 

  int magic;

  boolean valid[MaxNet];

  char SSIDs[MaxNet][100];

  int alive_days;

  int last_time_feeded;

} Networks;

 

FlashStorage(my_flash_store, Networks);

Networks values;

 

void setup() {

 

  Serial.begin(115200);

  delay(5000);

//  Serial.println("Entering Setup...");

  pinMode(redPin, OUTPUT);

  pinMode(greenPin, OUTPUT);

  pinMode(bluePin, OUTPUT);

   

  rtc.begin(); // enable real time clock functionalities

 

  // set up a WakeUp function, it will be triggered each time the Nerd exit the sleeping mode

LowPower.attachInterruptWakeup(RTC_ALARM_WAKEUP, WakeUp, CHANGE);

 

  values = my_flash_store.read(); // Read values from flash memory

  if (values.magic == MAGIC_NUMBER)  // If token is correct print saved networks

  {

    Serial.println("saved data:");

    Serial.println("");

    for (int a = 0; a < MaxNet; a++)

    {

      if (values.valid[a])

      {

        Serial.println(values.SSIDs[a]);

      } else {

        PosToBeSaved = a;

      }

    }

  }

//  Serial.println("Leaving Setup!");

}

 

void loop() {

  int SensorValue=analogRead(SensorPin);

 

  Serial.println(SensorValue);

  // Awaking notification

  if(SensorValue>30 && justWokeUp){

    justWokeUp=false;

    setColor(0, 255, 0); // green

    tone(BuzzerPin, 31, 200); // tone(Pin, Note, Duration);

    delay(200);

    setColor(0, 0, 0); // off

    noTone(BuzzerPin);

    delay(1000);

}

 

   // Check if the Nerd has been fed within 2 days

  if(rtc.getEpoch() - values.last_time_feeded >= 86400*2){ // 86400 is the number of seconds in one day

    // DIE

    SOS();

    values.alive_days = 0 ;

    values.last_time_feeded = rtc.getEpoch();

    empty_network_array();

  }

   

  // End of the day, empty the network array and go to sleep

  if(rtc.getHours() == 23 && rtc.getMinutes() >= 30){

    // Empty the array of network

    values.alive_days +=1;

    empty_network_array();

    hungry=true;

    LowPower.sleep(3600*8); // sleep 8 hours

  }

   

 

  if(!atHome) check_home();

  if (atHome && hungry) {

    Serial.println("checking for network");

     

    // Temporarly save the number of networks

    int networks_already_saved = PosToBeSaved;

     

    getNetwork();

     

    // compare the two values and complain if no new network is detected

    if (networks_already_saved == PosToBeSaved) SOS();

     

    if(PosToBeSaved >= daily_amount_of_food) hungry=false; // check if had enough food

    if(SensorValue < 30)  LowPower.sleep(sleeping_time); // snooze if dark

  }

  else if(atHome && !hungry) {

    Serial.println("back to sleep");

    LowPower.sleep(sleeping_time);

  }

  else if(atHome && hungry){

    if(SensorValue < 30)  LowPower.sleep(sleeping_time); // back to sleep if it's dark

    SOS();

  }

   

  // Set color status feedback

  if(PosToBeSaved >= 8){ // if starving show red

  setColor(255, 0, 0); // Red

  }

  else if(PosToBeSaved > 4 && PosToBeSaved < 8){

  setColor(255, 255, 0); // yellow

  }

  else{

  setColor(0, 255, 0); // green

  }

   

}

   

void WakeUp() {

  Serial.println("awake");

  atHome = false;

  justWokeUp =true;

}

 

void check_home() {

  int numSsid = WiFi.scanNetworks();

  if (numSsid != -1) {

    for (int thisNet = 0; thisNet < numSsid; thisNet++) {

      delay(100);

      if (strncmp(WiFi.SSID(thisNet), home_ssid, 100) == 0) {

        Serial.println("Yay, I'm home \n");

        atHome = true;

        connect_WiFi();

      }

    }

  }

  Serial.println("Leaving check_home!");

}

 

void connect_WiFi() {

  if (WiFi.status() != WL_CONNECTED) {

    while (WiFi.begin(home_ssid, password) != WL_CONNECTED) {

      delay(500);

    }

    Serial.print("WiFi connected: ");

    Serial.println(home_ssid);

    GetCurrentTime();

    printTime();

  }

}

 

// Feed the Nerd with networks's SSID

void getNetwork() {

  // scan for nearby networks:

  Serial.println("*Scan Networks*");

  int numSsid = WiFi.scanNetworks();

  delay(1000);

  if (numSsid == -1)

  {

    Serial.println("There are no WiFi networks here..");

  } else {

    Serial.print("number of available networks: ");

    Serial.println(numSsid);

    // print the network number and name for each network found:

    for (int thisNet = 0; thisNet < numSsid; thisNet++) {

      Serial.print("SSID: ");

      Serial.println(WiFi.SSID(thisNet));

      delay(500);

 

      char* net = WiFi.SSID(thisNet);

      bool canBeSaved = true;

 

      // check if the network has already been saved

      for (int a = 0; a < PosToBeSaved ; a++) {

        if (values.valid[a]) {

          if (strncmp(net, values.SSIDs[a], 100) == 0 || strnlen(net, 100) == 0) {

            Serial.println("Not saved");

            canBeSaved = false;

          }

        }

      }

       

      // Store ssid name

      if (canBeSaved && PosToBeSaved < MaxNet) {

        if (strlen(net) + 1 < 100 && strlen(net) > 0) { // check if the SSID name fits 100 bytes

          memset(values.SSIDs[PosToBeSaved], 0, sizeof(values.SSIDs[PosToBeSaved])); // set all characters to zero

          memcpy(values.SSIDs[PosToBeSaved], net, strlen(net) + 1); // copy "net" to values.SSDs[thisNet]

          values.valid[PosToBeSaved] = true;

          values.last_time_feeded = rtc.getEpoch();

          values.magic = MAGIC_NUMBER;

          my_flash_store.write(values);

          Serial.println(String(values.SSIDs[PosToBeSaved]) + " saved in position " + String(PosToBeSaved));

          PosToBeSaved ++;

        }

        else {

          Serial.println(" network skipped");

        }

      }

    }

  }

}

 

// Reset the array in which networks are saved

void empty_network_array(){

  for(int a = 0; a < PosToBeSaved; a++ ){

    values.valid[a] = false;

  }

    values.magic = 0;

    my_flash_store.write(values);

}

 

void SOS(){

  for(int a = 0; a< 3; a++){ 

    setColor(255, 0, 0); // Red

    tone(BuzzerPin, 31, 100); // tone(Pin, Note, Duration);

    delay(100);

    setColor(0, 0, 0); // off

    noTone(BuzzerPin);

    delay(50);

  }

  delay(1000);

  for(int a = 0; a< 3; a++){ 

    setColor(255, 0, 0); // Red

    tone(BuzzerPin, 31, 2000); // tone(Pin, Note, Duration);

    delay(1000);

  }

  for(int a = 0; a< 3; a++){ 

    setColor(255, 0, 0); // Red

    tone(BuzzerPin, 31, 100); // tone(Pin, Note, Duration);

    delay(100);

    setColor(0, 0, 0); // off

    noTone(BuzzerPin);

    delay(50);

  }

  delay(10000);

}

 

// Send RGB values to the LED pins

void setColor(int red, int green, int blue){

analogWrite(redPin, red);

analogWrite(greenPin, green);

analogWrite(bluePin, blue);  

}

 

  1. GetCurrentTime.ino

 

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

  Start an UDP connection to get the time in unix,

  then set the real time clock (rtc)

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

 

 

unsigned int localPort = 2390;      // local port to listen for UDP packets

IPAddress timeServer(129, 6, 15, 28); // time.nist.gov NTP server

const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message

byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets

const int GMT = 1 * 60 * 60; //change this to adapt it to your time zone hours*minutes*seconds

 

unsigned long epoch;

 

void GetCurrentTime() {

 

  int numberOfTries = 0, maxTries = 6;

  do {

    epoch = readLinuxEpochUsingNTP();

    numberOfTries++;

  }

  while ((epoch == 0) || (numberOfTries < maxTries));

 

  if (numberOfTries < maxTries) {

    Serial.print("NTP unreachable!!");

    while (1);

  }

  else {

    Serial.print("Epoch received: ");

    Serial.println(epoch);

    rtc.setEpoch(epoch + GMT);

  }

}

 

unsigned long readLinuxEpochUsingNTP()

{

  Udp.begin(localPort);

  sendNTPpacket(timeServer); // send an NTP packet to a time server

  // wait to see if a reply is available

  delay(1000);

 

  if ( Udp.parsePacket() ) {

    Serial.println("NTP time received");

    // We've received a packet, read the data from it

    Udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer

 

    //the timestamp starts at byte 40 of the received packet and is four bytes,

    // or two words, long. First, esxtract the two words:

 

    unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);

    unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);

    // combine the four bytes (two words) into a long integer

    // this is NTP time (seconds since Jan 1 1900):

    unsigned long secsSince1900 = highWord << 16 | lowWord;

 

    // now convert NTP time into everyday time:

    // Unix time starts on Jan 1 1970. In seconds, that's 2208988800:

    const unsigned long seventyYears = 2208988800UL;

    // subtract seventy years:

 

    Udp.stop();

    return (secsSince1900 - seventyYears);

  }

 

  else {

    Udp.stop();

    return 0;

  }

}

 

// send an NTP request to the time server at the given address

unsigned long sendNTPpacket(IPAddress & address)

{

  // set all bytes in the buffer to 0

  memset(packetBuffer, 0, NTP_PACKET_SIZE);

  // Initialize values needed to form NTP request

  // (see URL above for details on the packets)

 

  packetBuffer[0] = 0b11100011;   // LI, Version, Mode

  packetBuffer[1] = 0;     // Stratum, or type of clock

  packetBuffer[2] = 6;     // Polling Interval

  packetBuffer[3] = 0xEC;  // Peer Clock Precision

  // 8 bytes of zero for Root Delay & Root Dispersion

  packetBuffer[12]  = 49;

  packetBuffer[13]  = 0x4E;

  packetBuffer[14]  = 49;

  packetBuffer[15]  = 52;

 

  // all NTP fields have been given values, now

  // you can send a packet requesting a timestamp:

  Udp.beginPacket(address, 123); //NTP requests are to port 123

  Udp.write(packetBuffer, NTP_PACKET_SIZE);

  Udp.endPacket();

}

 

void printTime() {

  // Print date...

  Serial.print(rtc.getDay());

  Serial.print(" / ");

  Serial.print(rtc.getMonth());

  Serial.print(" / ");

  Serial.print(rtc.getYear());

  Serial.print("\t");

 

  // ...and time

  print2digits(rtc.getHours());

  Serial.print(": ");

  print2digits(rtc.getMinutes());

  Serial.print(": ");

  print2digits(rtc.getSeconds());

  Serial.println("");

}

 

void print2digits(int number) {

  if (number < 10) {

    Serial.print("0");

  }

  Serial.print(number);

}

  imageimage

Aside from the flashing LED and buzzing buzzer, the sole output is displayed through the serial monitor.

  image

   

Tutorial #5 – Plant Communicator

It seems as you progress through the tutorials the complexity increases, but the quality of the documentation decreases.  Again this is the case with the Plant Communicator.  Great potential, great disappointment.  The goal is to monitor the temperature, light and soil moisture of a plant.  When the moisture level drops below a certain level a message is sent over a WiFi connection to the online automation tool, Zapier, which in turn sends an email warning you to water your plant.  Cool!  This is another tutorial that needs the desktop IDE to compile due to include files availability.  However the program has too many bugs that it takes a lot of debugging to get it to even connect to the server.  I spent many days trying to track down why the program wouldn’t connect to the server.  I had partial success by removing the #include<WiFiSSLClient.h> line from the program and relying on the WiFiClient class from the WiFi101.h file. But only partial success.  From the community comments I’m not the only one with connection problems.  This is where having links to key commands would be helpful in the tutorial documentation.

 

The partial-final code for the “Plant Communicator” tutorial is shown below:

 

5_Plant_Communicator

 

#include "arduino_secrets.h"

#include <WiFi101.h>

//#include<WiFiSSLClient.h>

#include <RTCZero.h>

 

const char* ssid = SECRET_SSID;    //  your network SSID (name)

const char* password = SECRET_PSWD;  // your network password

String httpsRequest = SECRET_REQUEST; // your Blynk API token

const char* host = "hooks.zapier.com";

//WiFiSSLClient client;

WiFiClient client;

 

RTCZero rtc; // create RTC object

 

/* Change these values to set the current initial time */

const byte seconds = 0;

const byte minutes = 0;

const byte hours = 16;

 

/* Change these values to set the current initial date */

const byte day = 4;

const byte month = 12;

const byte year = 17;

 

 

int lightPin = A5; //the analog pin the light sensor is connected to

int tempPin = A4; //the analog pin the TMP36's Vout (sense) pin is connected to

int moisturePin= A6;

 

// Set this threeshold accordingly to the resistance you used

// The easiest way to calibrate this value is to test the sensor in both dry and wet earth

int threshold= 400;

 

bool alert_already_sent=false;

bool email_already_sent=true;

 

void setup() {

    Serial.begin(9600);

    while(!Serial);

    delay(2000);

    Serial.print("Connecting Wifi: ");

    Serial.println(ssid);

    while (WiFi.begin(ssid, password) != WL_CONNECTED) {

      Serial.print(".");

      delay(500);

    }

  Serial.println("");

  Serial.println("WiFi connected");

   

   

  rtc.begin(); // initialize RTC 24H format

  rtc.setTime(hours, minutes, seconds);

  rtc.setDate(day, month, year);

  rtc.setAlarmTime(16, 1, 0);  // Set the time for the Arduino to send the email

  rtc.enableAlarm(rtc.MATCH_HHMMSS);

  rtc.attachInterrupt(alarmMatch);

}

 

void loop() {

   

  Serial.print(get_temperature());

  Serial.println(" degrees C ");

  Serial.print(get_light());

  Serial.println(" light");

  Serial.print(get_average_moisture());

  Serial.println(" moisture level");

  Serial.println("");

   

  delay(1000);

   

  String warning="";

  // Send an extra email only if the plant needs to be waterd

  if(get_average_moisture() < threshold && !alert_already_sent){

    warning ="Warning your plant needs water !"; // Insert here your emergency message

    warning.replace(" ", "%20");

    send_email(get_temperature(), get_average_moisture(),get_light(), warning);

    alert_already_sent=true; // Send the alert only once

  }

   

  // Send the daily email

  if(!email_already_sent){

    send_email(get_temperature(), get_average_moisture(),get_light(), warning);

    email_already_sent=true;

  }

 

}

 

float get_temperature(){

  int reading = analogRead(tempPin); 

  float voltage = reading * 3.3;

  voltage /= 1024.0;

   

// Print tempeature in Celsius

float temperatureC = (voltage - 0.5) * 100 ; //converting from 10 mv per degree wit 500 mV offset

 

// Convert to Fahrenheit

float temperatureF = (temperatureC * 9.0 / 5.0) + 32.0;

 

return temperatureC;

}

int get_average_moisture(){ // make an average of 10 values to be more acurate

   

  int tempValue=0; // variable to temporarly store moisture value

  for(int a=0; a<10; a++){

    tempValue+=analogRead(moisturePin);

    delay(10);

  }

  return tempValue/10;

}

int get_light(){

  int light_value=analogRead(A0);

  return light_value;

}

 

void alarmMatch(){ // triggered when the alarm goes on

  Serial.println("Alarm Match!");

  email_already_sent = false;

  alert_already_sent = false;

}

 

void send_email(float temperature, int moisture, int light, String warning){

   

  // convert values to String

  String _temperature = String(temperature);

  String _moisture = String(moisture);

  String _light = String(light);

  String _warning = warning;

 

  if (client.connect(host, 443)) {

    Serial.println("Sending Client Request...");

    client.println("POST "+httpsRequest+"temperature="+_temperature+"&moisture="+_moisture+"&light="+_light+"&warning="+_warning+" HTTP/1.1");

    client.println("Host: "+ String(host));

    client.println("Connection: close");

    client.println();

     

    delay(1000);

    while (client.available()) { // Print on the console the answer of the server

      char c = client.read();

      Serial.write(c);

    }

    client.stop();  // Disconnect from the server

  }

  else {

    Serial.println("Failed to connect to client");

  }

}

imageimage

     

What I didn’t try (much)

I was interested in digging deeper into the different application, but ran out of time.  The tutorials cover only a beginner’s view point of some of the many features. 

 

What’s next?

I’m interested in trying other projects for the MKR1000 on the Arduino Project hub or even create one myself and as mentioned above, dig a little deeper.  One of the great things about the hub is the input from all the open source contributors.

 

Please let me know if I missed something in the documentation.  Also please pardon my typos.

 

Gordon Margulieux

Meridian, ID USA

Anonymous