RoadTest: Arduino MKR1000 IoT Bundle Kit
Author: gpolder
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?: all other arduino and arduino alike boards
What were the biggest problems encountered?: Logic is 3.3 Volt, so I ordered a level switcher. Not a big issue, but all arduino boards I used up till now had 5V logic. The supplied breadboard was too small to place the Arduino together with the display.
Detailed Review:
First, I want to thank Element14 and Arduino for giving me the opportunity to review the Arduino MKR1000 IoT Bundle Kit. It's a very nice kit, from which I only used part of the supplied parts for my project.
Long long ago I made a web based interface to an old mobile radio and converted it to the ham radio two meter band.
Design was based on the TINI, a java based network controller from Dallas.
Details here: Agri Vision - WEB enabled KF161
For the roadtest I proposed to upgrade the design by replacing the TINI by this Arduino MKR1000, where the IoT bundle kit really can help as interface to the radio hardware.
In the application I scheduled the project as follows:
{gallery} Arduino MKR1000 IoT Bundle Kit - Unboxing |
---|
The original TINI based hardware setup as depicted in the drawing above consists of two units; the Bosh KF161 radio itself, in which I placed the pcf8587 as replacement of the rom, and a max127 ADC to readout switches and strength of the received signal. The I2C bus was connected to a separate box, holding the TINI, display.
The I2C bus has two connectors, one direct to the TINI, the other one using opto-isolators. For the new setup I will put everything in the cabinet of the radio and power from the same supply.
How the max127 and pcf8587 are interfaced in the radio itself is described in another section, down in this review.
Some pictures of the current setup:
Original PCB with I2C connectors, Opto-isolators and display.
As a start of the roadtest I put the MKR1000 on the provided breadboard.
Not a big deal I would say, just connect the I2C SCL and SDA lines to the existing circuit.
There unfortunately is one issue, as the logic of the TINI controller is at 5V, while the MKR1000 operates at 3.3V.
I could have used the opt-isolation from the original project also for level conversion, but since I decided to run everything from one power supply within one box, I decided to use transistor based level converters, so I ordered and installed an I2C compatible level converter and connected the existing LCD display.
The display was removed from the original display and connected to the level converters on the breadboard.
The LCD display is driven by a PCF 2103 controller. I converted the JAVA code from the TINI to Arduino code.
#include <Wire.h> byte DISPLAY_ADDRESS = 0x3A; void dispLCD(int line, String str) { char charBuf[str.length() + 1]; str.toCharArray(charBuf, str.length() + 1); for (int i = 0; i < str.length(); i++) { charBuf[i] = (byte)(0x80 + str.charAt(i)); } Wire.beginTransmission(DISPLAY_ADDRESS); Wire.write((byte)0x00); // sends instruction byte if (line == 2) Wire.write((byte)0xC4); else Wire.write((byte)0x84); Wire.endTransmission(); // stop transmitting delay(100); Wire.beginTransmission(DISPLAY_ADDRESS); Wire.write((byte)0x40); // sends instruction byte Wire.write(charBuf); Wire.endTransmission(); // stop transmitting } void display_init(){ Wire.beginTransmission(DISPLAY_ADDRESS); // transmit to device 0x3A // device address is specified in datasheet Wire.write((byte)0x00); // sends instruction byte Wire.write((byte)0x25); Wire.write((byte)0x06); Wire.write((byte)0x24); Wire.write((byte)0x0C); Wire.endTransmission(); // stop transmitting } void setup() { Wire.begin(); // join i2c bus (address optional for master) display_init(); dispLCD(1, "hello!!!"); dispLCD(2, "12345678"); } void loop() { }
As can be seen (barely) on the image above this code works, "hello" and 1-8 are shown on the display. This is a proof that the I2C and level converter works, but I'm not very happy with the display. It is too small and only visible from an angle of 90 degrees from the front. For this reason I switched to the display which was supplied as part of the kit.
Drawback of this display that it has a parallel interface, so a lot of connections needs to be made. I'v put another breadboard with the display next to the first one.
From the second experiment mentioned on the MKR1000 IoT bundle homepage (https://create.arduino.cc/projecthub/arduino/puzzlebox-c1f374) I learned how to connect it to the MKR1000. I only had to change pin 11 (en) and 12 (rs) to pin 13 and 14, as 11 and 12 are used for the I2C bus in my case. Although the display is a 5V device, it could perfectly be controlled by the 3.3V IO levels of the MKR1000.
Code to test the display, slightly adapted from https://create.arduino.cc/projecthub/arduino/puzzlebox-c1f374:
#include <LiquidCrystal.h> // initialize the library by associating any needed LCD interface pin // with the arduino pin number it is connected to const int rs = 14, en = 13, d4 = 2, d5 = 3, d6 = 4, d7 = 5; LiquidCrystal lcd(rs, en, d4, d5, d6, d7); void setup() { analogWrite(A3, 75); // Set the brightness to its maximum value // set up the LCD's number of columns and rows: lcd.begin(16, 2); // Print a message to the LCD. lcd.print("hello, world!"); } void loop() { // set the cursor to column 0, line 1 // (note: line 1 is the second row, since counting begins with 0): lcd.setCursor(0, 1); // print the number of seconds since reset: lcd.print(millis() / 1000); }
And here the result:
As one can see the supplied breadboard is too small for placing the MKR1000 and the display. I luckily had another breadboard around, which I connected to the breadboard from the kit.
In order to connect the I2C bus from the radio to the MKR1000, I initially used the original PCB with the connecters. As the level converters doesn't work when the opto-isolators also are connected to the bus (presumably due to the extra pull-up resistors), I disconnected them from the bus, and use the upper direct connected connector for connecting the MKR1000 to the radio.
As can be seen in the schematic diagram in one of the next paragraphs, the channel switch is modified using a resistor bank, in order to output an analog value to one of the channels of the max127.
I wrote a small test script that reads this analog value and outputs it on the serial console:
#include <Wire.h> byte MAX127_ADDRESS = 0x28; int reading = 0; void setup() { Wire.begin(); Serial.begin(9600); } void loop() { Wire.beginTransmission(MAX127_ADDRESS); Wire.write((byte)0x80); Wire.endTransmission(); // stop transmitting // request reading from ADC Wire.requestFrom(MAX127_ADDRESS, 2); // request 2 bytes from slave device #112 // receive reading from sensor if (2 <= Wire.available()) { // if two bytes were received reading = Wire.read(); // receive high byte (overwrites previous reading) reading = reading << 8; // shift high byte to be high 8 bits reading |= Wire.read(); // receive low byte as lower 8 bits } Serial.print("Switch ADC Value: "); Serial.println(reading); delay(1000); }
Here is the output in the console when turning the band switch:
Which proves the max127 works.
The desired frequency of the radio in MHz can be set by subtracting 10.7 and divide the result by 1.25. The 16 bit result is placed at the original prom socket outputs.
I divided these 16 IO lines in A and B, each controlled by a pcf8587 IO extender, as can be seen in the schematic above.
The Arduino code was adapted from the TINI JAVA code, in this case the function setfreq. In this case only the I2C control is adapted, the rest of the code appeared to be the same in JAVA and C++ (Arduino).
#include <Wire.h> byte pcf8587_ADDRESS_A = 0x38; byte pcf8587_ADDRESS_B = 0x39; void setFreq(double freq) { //frequency in MHz int adata, bdata; double ba = (freq - 10.7) / 1.25; bdata = (int)ba; adata = (int)(100 * (ba - bdata)); bdata ^= 0xFF; bdata &= 0x7F; adata ^= 0xFF; adata &= 0x7F; Wire.beginTransmission(pcf8587_ADDRESS_A); Wire.write((byte)adata); Wire.endTransmission(); Wire.beginTransmission(pcf8587_ADDRESS_B); Wire.write((byte)bdata); Wire.endTransmission(); } void setup() { Wire.begin(); setFreq(145.275); } void loop() { // put your main code here, to run repeatedly: }
In order to test if this works I transmitted a signal at 145.275 using a handheld radio, and indeed this signal was received by the radio, as can be seen in the picture below.
This proves the pcf8587 IO extenders works.
The user interface of the MKR1000 controlled radio, now consists of the channel selection switch, and the display.
The channel switch has 10 positions, therefore I made an array with ten predefined frequencies. The display shows the selected channel, the frequency and the strength of the received signal.If no signal is received, 'arduino' is displayed.
Here is the complete code:
#include <LiquidCrystal.h> #include <Wire.h> #include <math.h> // initialize the library by associating any needed LCD interface pin // with the arduino pin number it is connected to const int rs = 14, en = 13, d4 = 2, d5 = 3, d6 = 4, d7 = 5; LiquidCrystal lcd(rs, en, d4, d5, d6, d7); byte MAX127_ADDRESS = 0x28; byte pcf8587_ADDRESS_A = 0x38; byte pcf8587_ADDRESS_B = 0x39; char lcdline0[20]; char lcdline1[20]; float freq[ 10 ] = { 144.500, 144.800, 145.250, 145.275, 145.300, 145.400, 145.500, 145.575, 145.625, 145.800 } ; String meter[8] = { "arduino ", "1 ", ">3 ", ">>5 ", ">>>7 ", ">>>>9 ", ">>>>>20 ", ">>>>>>40" }; int currentswitch; void setFreq(double freq) { //frequency in MHz int adata, bdata; double ba = (freq - 10.7) / 1.25; bdata = (int)ba; adata = (int)(100 * (ba - bdata)); bdata ^= 0xFF; bdata &= 0x7F; adata ^= 0xFF; adata &= 0x7F; Wire.beginTransmission(pcf8587_ADDRESS_A); Wire.write((byte)adata); Wire.endTransmission(); Wire.beginTransmission(pcf8587_ADDRESS_B); Wire.write((byte)bdata); Wire.endTransmission(); } int getswitch() { int reading = 0; Wire.beginTransmission(MAX127_ADDRESS); Wire.write((byte)0x80); Wire.endTransmission(); // stop transmitting // request reading from ADC Wire.requestFrom(MAX127_ADDRESS, 2); // request 2 bytes from slave device #112 // receive reading from sensor if (2 <= Wire.available()) { // if two bytes were received reading = Wire.read(); // receive high byte (overwrites previous reading) reading = reading << 8; // shift high byte to be high 8 bits reading |= Wire.read(); // receive low byte as lower 8 bits } return (reading >> 12) + 1; } void updateMeter() { int reading[2]; int sig; Wire.beginTransmission(MAX127_ADDRESS); Wire.write((byte)0x90); Wire.endTransmission(); // stop transmitting // request reading from ADC Wire.requestFrom(MAX127_ADDRESS, 2); // request 2 bytes from slave device #112 // receive reading from sensor if (2 <= Wire.available()) { // if two bytes were received reading[1] = Wire.read(); // receive high byte (overwrites previous reading) reading[0] = Wire.read(); // receive low byte as lower 8 bits } sig = min((int)log((reading[0]<<4)+(reading[1]>>4)+1),7); // convert value to signal strength lcd.setCursor(8,0); lcd.print(meter[sig]); } void updateChannel() { String str_temp; currentswitch = getswitch(); setFreq(freq[currentswitch - 1]); lcd.setCursor(0, 1); str_temp = String(freq[currentswitch - 1], 3); sprintf(lcdline1, "%-2d - %s", getswitch(), str_temp.c_str()); lcd.print(lcdline1); } void setup() { Serial.begin(9600); Wire.begin(); analogWrite(A3, 75); // Set the brightness to its maximum value // set up the LCD's number of columns and rows: lcd.begin(16, 2); // Print a message to the LCD. lcd.print("KF161 - Arduino"); updateChannel(); } void loop() { if (currentswitch != getswitch()) { updateChannel(); } updateMeter(); }
In the proposal plan was the connection of the circuit to the radio one off the last points. But this step was already done long ago when I made this for the TINI board.
And of course I'v been using the interface in the previous steps. In order to stick to my original plan, here I present the modification of the radio.
Originally the radio consists of a frequency synthesiser, controlled by a prom. For the modification the prom is replaced by io-extenders which control the dividers of the synthesiser and as such the frequency.
To read the channel switch position, I added a resistor bank, which output was read by a max127 ADC. Also the signal strength which is available on one of the testpoints in the radio is connected to the max127.
How this looks like in reality can be seen in the pictures below.
Next step is to make an interface to the internet. When using the TINI, back in 2001, the internet of things wasn't invented yet, so I implemented a webserver on the TINI and a Java Applet to control the radio from internet. This looked like:
Nowadays a lot of IoT platforms are available, from which I choose https://blynk.io/ . Mainly because it's usage looks very simple and a nice demo (https://create.arduino.cc/projecthub/arduino/puzzlebox-c1f374 ) was mentioned on the the MKR1000 IoT Bundle Kit page (https://store.arduino.cc/arduino-iot-mkr1000-bundle ).
First I individually tested the Blynk connection using sample code generated from the Blynk website:
/************************************************************* Download latest Blynk library here: https://github.com/blynkkk/blynk-library/releases/latest Blynk is a platform with iOS and Android apps to control Arduino, Raspberry Pi and the likes over the Internet. You can easily build graphic interfaces for all your projects by simply dragging and dropping widgets. Downloads, docs, tutorials: http://www.blynk.cc Sketch generator: http://examples.blynk.cc Blynk community: http://community.blynk.cc Follow us: http://www.fb.com/blynkapp http://twitter.com/blynk_app Blynk library is licensed under MIT license This example code is in public domain. ************************************************************* This example shows how to use Arduino MKR1000 to connect your project to Blynk. Note: This requires WiFi101 library from http://librarymanager/all#WiFi101 Feel free to apply it to any other example. It's simple! *************************************************************/ /* Comment this out to disable prints and save space */ //#define BLYNK_DEBUG // Optional, this enables lots of prints #define BLYNK_PRINT SerialUSB #include <SPI.h> #include <WiFi101.h> #include <BlynkSimpleMKR1000.h> // You should get Auth Token in the Blynk App. // Go to the Project Settings (nut icon). char auth[] = "xxxxxxxxxxxx"; // your Blynk API token // Your WiFi credentials. // Set password to "" for open networks. char ssid[] = "xxxxxxxxxx"; char pass[] = "xxxxxxxxxx"; void setup() { // Debug console SerialUSB.begin(9600); //Blynk.begin(auth, ssid, pass); // this doesn't work // You can also specify server: Blynk.begin(auth, ssid, pass, "blynk-cloud.com", 8080); //I selected this one, using port 8080, which works fine. //Blynk.begin(auth, ssid, pass, IPAddress(192,168,1,100), 8080); } void loop() { Blynk.run(); }
Unfortunately this didn't give a persistent connection. I got a whole lot of connection timeouts:
It connected once and a while, but immediately disconnected again. Before implementing this in the radio, it needs to be working properly, otherwise I have to select an alternative IoT platform.
This didn't give a persistent connection in first instance, but after switching from port 80 to 8080, all went well.
The interesting thing is that with blynk I could program IO ports without changing anything in the program above.
In blynk I created a push button for toggling the onboard LED:
Which proves that blynk does it's job.
Now blynk is working, I made a nice user interface, with a 'Labeled Value' to display the channel number and frequency, a 'Gauge' for displaying the signal strength and two 'Numeric Inputs' one for MHz and one for kHz from which an arbitrary frequency can be set from the app.
The controles are interfaced to the Arduino via virtual pin's, using just a few lines of code. In my opinion this is one of the strong points of a IoT platform like this. Creating this interface and the app took me a fraction of the time I needed for the old implementation in the TINI.
I will not describe the details on how to construct the app, this is well written in the blynk documentation (https://blynk.io/ ).
Here is the code of the complete Arduino application:
#include <LiquidCrystal.h> #include <Wire.h> #include <math.h> #include <WiFi101.h> #include <BlynkSimpleMKR1000.h> const char* ssid = "Polderweg"; // your network SSID (name) const char* password = "alleenvandaag"; // your network password char auth[] = "GwfS9SB37zhDQIcAwXo3dx3Z8Cj_KiSP"; // your Blynk API token #define BLYNK_PRINT Serial // initialize the library by associating any needed LCD interface pin // with the arduino pin number it is connected to const int rs = 14, en = 13, d4 = 2, d5 = 3, d6 = 4, d7 = 5; LiquidCrystal lcd(rs, en, d4, d5, d6, d7); byte MAX127_ADDRESS = 0x28; byte pcf8587_ADDRESS_A = 0x38; byte pcf8587_ADDRESS_B = 0x39; char lcdline0[20]; char lcdline1[20]; float freq[ 10 ] = { 144.500, 144.800, 145.250, 145.275, 145.300, 145.400, 145.500, 145.575, 145.625, 145.800 } ; String meter[8] = { "arduino ", "1 ", ">3 ", ">>5 ", ">>>7 ", ">>>>9 ", ">>>>>20 ", ">>>>>>40" }; int currentswitch; int freqMHz; int freqkHz; int sig = 0; BLYNK_READ(V0) // Widget in the app READs Virtal Pin V0 with the certain frequency { Blynk.virtualWrite(0, sig); } // Blynk functions to retrieve values BLYNK_WRITE(V2) { freqMHz = param.asInt(); } BLYNK_WRITE(V3) { freqkHz = param.asInt(); } void setFreq(double freq) { //frequency in MHz int adata, bdata; double ba = (freq - 10.7) / 1.25; bdata = (int)ba; adata = (int)(100 * (ba - bdata)); bdata ^= 0xFF; bdata &= 0x7F; adata ^= 0xFF; adata &= 0x7F; Wire.beginTransmission(pcf8587_ADDRESS_A); Wire.write((byte)adata); Wire.endTransmission(); Wire.beginTransmission(pcf8587_ADDRESS_B); Wire.write((byte)bdata); Wire.endTransmission(); } int getswitch() { int reading = 0; Wire.beginTransmission(MAX127_ADDRESS); Wire.write((byte)0x80); Wire.endTransmission(); // stop transmitting // request reading from ADC Wire.requestFrom(MAX127_ADDRESS, 2); // request 2 bytes from slave device #112 // receive reading from sensor if (2 <= Wire.available()) { // if two bytes were received reading = Wire.read(); // receive high byte (overwrites previous reading) reading = reading << 8; // shift high byte to be high 8 bits reading |= Wire.read(); // receive low byte as lower 8 bits } return (reading >> 12) + 1; } void updateMeter() { int reading[2]; Wire.beginTransmission(MAX127_ADDRESS); Wire.write((byte)0x90); Wire.endTransmission(); // stop transmitting // request reading from ADC Wire.requestFrom(MAX127_ADDRESS, 2); // request 2 bytes from slave device #112 // receive reading from sensor if (2 <= Wire.available()) { // if two bytes were received reading[1] = Wire.read(); // receive high byte (overwrites previous reading) reading[0] = Wire.read(); // receive low byte as lower 8 bits } sig = min((int)log((reading[0] << 4) + (reading[1] >> 4) + 1), 7); // convert value to signal strength lcd.setCursor(8, 0); lcd.print(meter[sig]); } void updateChannel() { String str_temp; currentswitch = getswitch(); setFreq(freq[currentswitch - 1]); lcd.setCursor(0, 1); str_temp = String(freq[currentswitch - 1], 3); sprintf(lcdline1, "CH %-2d - %s", getswitch(), str_temp.c_str()); lcd.print(lcdline1); Blynk.virtualWrite(V5, lcdline1); } void updateFreq(float frequency) { String str_temp; setFreq(frequency); lcd.setCursor(0, 1); str_temp = String(frequency, 3); sprintf(lcdline1, "Blynk - %s", str_temp.c_str()); lcd.print(lcdline1); Blynk.virtualWrite(V5, lcdline1); } void setup() { Serial.begin(9600); Wire.begin(); analogWrite(A3, 75); // Set the brightness to its maximum value // set up the LCD's number of columns and rows: lcd.begin(16, 2); // Print a message to the LCD. lcd.print("KF161 - Arduino"); updateChannel(); Blynk.begin(auth, ssid, password, "blynk-cloud.com", 8080); } void loop() { if (currentswitch != getswitch()) { updateChannel(); } updateMeter(); int temp_freqMHz = freqMHz; int temp_freqkHz = freqkHz; Blynk.run(); // poll new combination values from the online app // check if combination values are changed and print them on the console if ((temp_freqMHz != freqMHz) || (temp_freqkHz != freqkHz)) { updateFreq(freqMHz + float(freqkHz) / 1000); } }
Below screenshots of the app, first one after selection of a channel using the switch on the radio, second one after selecting a frequency using the blynk app:
And here is a video which gives an impression of the functionality:
Hm, unfortunately not completely finished yet. But because the deadline is almost there, I decided to upload the current version of my review.
In the near future I will try Blynk again or switch to another IoT platform. In the past I did some testing with Cayene on a Raspberry Pi. Maybe I'll test that on the Arduino.
If everything fails, I can always implement a web server like I did on the TINI. With HTML5, Javascript or another browser tool, it is currently not necessary to write a Java applet like I did for the TINI.
Now the complete functionality which I had in mind for this roadtest review is implemented.
In the mean time another problem popped up, The Radio stopped working, probably something with the power supply unit which controls the voltages for transmit and receive.
I have to look into that as soon as possible.
26 September 2019: Blynk connection fixed and tested.
3 October 2019: Full functionality with control from blynk app implemented.
Top Comments
*
I updated the review because I got blynk working today by changing the port number from 80 to 8080.
Nice project. Real life projects often get messy until you sort out all the issues!