Getting Started

8 Dec 2022   by Mark W Kiehl / Mechatronic Solutions LLC

This information is relevent to using the blues.io Swan, Notecarrier-F, WiFi Notecard, and LTE Cat-1 Notecard hardware within a Windows OS environment.   All commands and JSON strings need to be adaped for other operating systems.  

Purchase

Before you purchase anything, I suggest you create an account by visiting notehub.io.   Then I suggest the purchase of the following:

  • Swan with loose header pins included.   This is a STM32L4 microcontroller. WARNING: The Swan has castellations along the entire edge, right next to the Feather pins, that are easy to accidentally include when you are soldering a header to the Swan.   If you are not fantastic at soldering, then consider purchasing the Swan option with headers.   The "with headers" option doesn't indicate if they are regular headers, or stacking headers, but I would assume the former.  
  • Notecarrier-F with included Molex antenna.   You attach a Notecard to this board.   At the bottom of the board it accepts the attachment of a Feather (such as the Swan).   If the Feather has stacking pins installed, you can also stack FeatherWing on top of it.   (I suggest a WiFi Notecard for development, and a cellular Notecard for production).  
  • Wi-Fi Notecard (so you can do developemnt over WifFi and not waste your Consumption Credits).   The Notecard Wi-Fi has a PCB antenna that is integrated directly into the Notecard itself.   A U.FL jack is also available for attaching an external antenna (not included).  
  • LTE Cat-1 Notecard | North America or whatever is appropriate for your region

The above was chosen because I like working with Feather & FeatherWing compatible products.   I like flexibility, I want high performance, and I'm frugal.  

When your package arrives, login to your account and put your Account ID and Billing ID in your e-wallet.   Then open a browser and go to https://dev.blues.io/.   Click on the Start the Quickstart button and review that material.   Open up your packages and look at all the QR codes on everything!   Since I chose Feather / FeatherWing based products, everything that follows is relevent to that only.  

Your Notecard F needs either a cellular Notecard, or a WiFi notecard attached to it.   Looking on the back of the smaller cards shipped, you will see one labeled "WiFi" and a QR code on the back of it, and another with a lable that doesn't make sense to me.   Plug the WiFi module into your Notecarrier F by following the similarly relevent instructions at: https://dev.blues.io/quickstart/notecard-quickstart/notecard-and-notecarrier-a/.  

The Notecarrier-F comes with a strip antenna with two connectors ("PN 698-8500MHZ&GPS" label, and "s21353" label) that is intended for your cellular Notecard.   The Molex PN 213353 antenna I received doesn't have any labels on it to identify which of the cables is for cellular, and which is for the GPS.  

From the datasheet, I was able to find a link to a product specification where the antenna cables were labeled, as shown below.  

 

A Note About Windows OS

All of the Notecard documentation and tutorial examples are formatted for use with the macOS or Linux operating system (OS).   JSON strings in maxOS/Linux OS are bounded by single quotes, and bounded by double quotes in the Windows OS.   JSON strings need to be escaped (prefix double quote with slash \) for Windwos OS when using the Windows CLI or for cURL command line requests (but not Postman).   cURL is installed with Windows 10 & 11.  

 

Setup Notecarrier-F with Notecard

Download and install the Notecard CLI (command-line utility) so you can interact with your Notecard from your browser over a USB connection.  

Connect the Noteccarrier-F to a USB cable plugged into your PC.   Verify communication between your PC and the Notecarrier-F by executing the following CLI commands:


notecard -version

notecard -info

 

Use the In-Browser Terminal to repeat communication verification with a JSON request:


{"req":"card.version"}

 

Do it one last time by using the Notecard CLI to execute the JSON request using the CLI "-req" command.   NOTE: Windows users will need to escape any double quotes in the JSON request, and surround the request with double quotes as shown below.   macOS & Linux OS users should surround the requests with a single quote.   Note that it is essential to include the '-verbose' option in order to see the responses from any requests made with "-req".  


notecard -verbose -req "{\"req\":\"card.version\"}"
notecard -verbose -req "{\"req\":\"card.wireless\"}"

 

Follow the guide to connecting to a WiFi access point to connect the WiFi Notecard to a 2.4 GHz WiFi access point.   Execute the following JSON request to connect your WiFi Notecard to your WiFi access point:


{"req":"card.wifi","ssid":"","password":""}

 

Use the 'hub.get' Notehub command to retrieve the current Notehub configuration for your Notecard.  


{"req":"hub.get"}

 

As of Notecard firmware v3.3.1 you can verify the WiFi Notecard is configured properly with the following JSON request:


{"req":"card.wireless"}

 

Troubleshooting WiFi

If the Notecard is connected to a WiFi hotspot and is in continuous mode, and then the power to the Notecard is cycled on/off, the Notecard will reconnect to the WiFi in continuous mode.   You can verify the WiFi is connected by issuing the JSON request:


{"req":"card.wireless"}

And evaluating the response to insure it includes: status":"{network-up}"

 

Robost WiFi Connection Procedure

You CANNOT clear any existing valid WiFi connection in 'continuous' mode with the following JSON request (documentation is incorrect):


{"req":"card.wifi","ssid":"-","password":"-"}

Instead, you must restart the Notecard with the following request:


{"req":"card.restart"}

If you follow that with a 'card.wireless' JSON request, the result will be: "status": "{wifi-join-wait}"

 

Establish a WiFi connection with the 'card.wifi' command:


{"req":"card.wifi","ssid":"[your ssid]","password":"[your password]"}

 

Make a 'hub.set' request and set the 'mode' to 'continuous':


{"req": "hub.set", "product":"com.your-company.your-name:your_product", "mode": "continuous"}

The response should be '{}' or empty and the green LED on the Notecard will illuminate continuously.  

 

Issue a 'hub.sync' request:


{"req":"hub.sync"}

The response should be '{}' or empty.

 

Confirm the WiFi connection with the 'card.wireless' request:


{"req":"card.wireless"}

If connected, the response should contain: "status":"{network-up}".   If status is "{wifi-join-wait}", then you didn't do a hub.sync, or your network credentials were wrong.   The green light on the WiFi Notecard should remain constantly on.  

 

From this point you can do a 'web.get' request or whatever you want to do.  

 

Setup Notehub

Setup a Notehub project and create a ProductUID.   A ProductUID is the unique identifier you will use to associate a Notecard to a Notehub Project.   Navigate to the Notehub Projects Dashboard and click on 'Create Project'.   When finished, take note of the ProductUID created.  

Next, configure your Notecard with the ProductUID you created.   Send a 'hub.set' JSON request to your notecard.  


{"req":"hub.set", "product":"com.your-company.your-name:your_product"}

 

Perform a manual synchronization to Notehub by sending this JSON request.  


{"req":"hub.sync"}

 

Send a synchronization status requests to the Notecard:


{"req":"hub.sync.status"}

 

Check the current status of the Notecard's connection to Notehub:


{"req": "hub.status"}

The hub Requests reference provides details on all of the hub request options.  

 

Queue & Send Notes

Add Notes to the Notecard, simulating sensor data sent to it by your MCU.  


{"req":"note.add","body":{"temp":35.5,"humid":56.23}}

 

Send the Notes from the Notecard to Notehub:


{"req":"hub.sync"}

Go to the Notehub and view the Notes by clicking on Events .  

 

Interact with Notehub via HTTP API

You will need your ProductUID, DeviceUID, and a Session Token in order to communicate with Notehub.   Get your ProductUID and DeviceUID by sending the JSON request:


{"req":"hub.get"}

 

Obtain a Session Token by sending the following Notehub API POST request using Postman, cURL, Python, or some other appropriate tool.   For Postman, configure a HTTP POST.   Windows 10 and 11 OS includes cURL.   Visit to learn how to use cURL.   The cURL command below is formatted for cURL running in Windows OS.   Note that the values for "username" and "password" should be urlencoded, and the double quotes within the JSON needs to be escaped.  


curl -- verbose --request POST --location https://api.notefile.net/auth/login --data "{\"username\":\"[you@youremail.com]\", \"password\": \"[your_password]\"}"

 

Use the HTTP API to queue a Note.   Substitute the 'X-SESSION-TOKEN', 'product', and 'device' fields in the request below with your values.


curl --request POST --location 'https://api.notefile.net/req' 
--header 'X-SESSION-TOKEN: [your_session_token]' \
--data '{"req":"note.add","file":"data.qi","body":{"api-key1":"api-val1"},
"product":"[com.your-company.your-name:your_product]","device":"[dev:000000000000000]"}'

 

 

Sync the Note on Notehub with your Notecard with 'hub.sync' and then confirm the sync is complete by using the 'hub.sync.status' command.


{"req":"hub.sync"}

{"req":"hub.sync.status"}

 

Your Note is stored on the Notecard in the file 'data.qi'.   Use the 'file.changes' request to determine if any changes to the file were made, and then use the command 'note.get' to print out the contents of the Note (peek), and then delete it (pop) with the '"delete":true' optional argument.   Follow that with another 'file.changes' to confirm that no more pending notes are inside 'data.qi'.  


{"req":"file.changes","files":["data.qi"]}

{"req":"note.get", "file":"data.qi","delete":true}

{"req":"file.changes","files":["data.qi"]}

 

Routes (webhook)

Notehub routes allow you to forward data from Notehub to your cloud service of choice via a HTTP POST (only).   See Route Tutorial.   You may also pull data out of Notehub from a remote service using a Notehub Event API.   In Notehub, choose the 'Routes' menu option, and then click on the '+ New Route' button.   Under 'Data', and 'Transform Data' you can select the type of transformation such as 'JSONata Expression', 'Flattened Event', 'Flattened Body Keys/Values', 'Body Only', and 'Payload Only'.  

 


Notecard Firmware Update

Execute the JSON request below while connected to your Notecarrier-F to get your Notecard firmware version.  


{"req":"card.version"}

Compare the Notecard firmware version reported to the Firmware Releases.   Download the latest firmware release.  

Updating the Notecard firmware over USB is performed using the STM32CubeProgrammer.   If you have a WiFi Notecard and a cellular Notecard, each needs to be updated.   Launch the built-in bootloader firmware by sending the 'bootloader' command via the terminal emulator.  


bootloader

The terminal emulator will disconnect from the Notecard.   Follow the Firmware update instructions.  

 


Notecard + MCU

The Swan Feather features a STM32L4 chip.   Code can be developed using a variety of tools including VS Code, the STM32CubeIDE, Arduino IDE, and CircuitPython.   Follow the blues.io Swan Quickstart for your preferred development environment.   The details that follow will be based on using the Arduino IDE.  

Arduino IDE

Follow the blues.io Using the Arduino IDE install instructions, BUT IT IS NOT NECESSARY TO INSTALL THE 'STM32CubeIDE' in order to use the Arduino IDE.   It is necessary to install the 'STM32CubeProgrammer'.   Note that if you plan to interact with the Swan via a micro USB cable, then make sure to follow the Without the STLink-V3Mini instructions, putting the Swan into DFU mode prior to attempting to upload a sketch from the Arduino IDE.  

NOTE: The Arduino IDE 'Tools -> Port' option is only relevent when you wish to use the Arduino serial monitor to see serial output from the Swan.   If you setup the serial port in the sketch on the Swan and code it to wait for the serial port to become available, the sketch wait infinitly for the serial port and never exit Setup() until you run the Arduino serial monitor.  


void setup() {
  Serial.begin(115200);
  while (!Serial) {
    delay(1);
  }

  Serial.println("Setup finished");
} // setup()

The sketch below will not cause the sketch to wait in Setup() for the serial port.


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

  Serial.println("Setup finished");
} // setup()

 

The sketch below demonstrates use of the built-in LED and pushbutton on the Swan


/*
  STM32L4 Feather
  Sold by blues.io (not an Adafruit product)
  
  https://dev.blues.io/quickstart/swan-quickstart/#installing-stm32duino-in-the-arduino-ide

  NOTE: v2.3.0 of STM32 does not compile.
 
 */

// In Arduino IDE, select 'Tools','Board','SM32 boards groups..','Blues Wireless boards'
// And then under 'Board part number' choose 'Swan R5'. 
// Tools -> USB support (if available), select 
// Find USB port in Windows by looking at ..."CDC (generic 'Serial' supersede U(S)ART)."

/////////////////////////////////////////////////////////////////////////
// Built in LED(s) & LiPoly Battery

// Red LED:  LED_BUILTIN is pin #56

// USER_BTN on pin 357 is a programmable button on the Swan
// that is pulled HIGH by default (LOW when pressed).

/////////////////////////////////////////////////////////////////////////
// blinkLEDnoDelay()
unsigned long LEDblinkPeriod = 8;
unsigned long LEDblinkLast = 0;
uint8_t LEDblinkPWM = 0;
bool LEDblinkState = false;
uint8_t LEDlastMode = 0;

void blinkLEDnoDelay(byte pin, byte mode) {
  // Blink the LED on 'pin' without using delay() according to
  // the 'mode' argument defined below. 
  // pin must support PWM. 
  // 
  // mode:
  //  0 = breathing
  //  1 = blink slow constantly
  //  2 = blink fast constantly
  //  3 = slow burst every 1 second
  //  4 = fast burst every 1 second
  //
  // Required global variables: LEDblinkPeriod, LEDblinkLast, LEDblinkPWM, LEDblinkState, LEDlastMode
  if (mode == 0) {
    // breathing
    LEDblinkPeriod = 8;
    if (LEDlastMode != mode) {
      LEDblinkPWM = 0;
      LEDblinkState = true;
      digitalWrite(pin, LOW);
    }
    if (millis() - LEDblinkLast >= LEDblinkPeriod) {
        if (LEDblinkPWM > 254) LEDblinkState = false;
        if (LEDblinkPWM < 1) LEDblinkState = true;
        if (LEDblinkState) {
            LEDblinkPWM++;
        } else {
            LEDblinkPWM--;
        }
        analogWrite(pin, LEDblinkPWM);
        LEDlastMode = mode;
        LEDblinkLast = millis();
    }
  } else if (mode == 1) {
    // blink slow constantly
    LEDblinkPeriod = 1000;
    if (millis() - LEDblinkLast >= LEDblinkPeriod) {
        digitalWrite(pin, LEDblinkState);
        LEDblinkState = !LEDblinkState;
        LEDlastMode = mode;
        LEDblinkLast = millis();
    }
  } else if (mode == 2) {
    // blink fast constantly
    LEDblinkPeriod = 100;
    if (millis() - LEDblinkLast >= LEDblinkPeriod) {
        digitalWrite(pin, LEDblinkState);
        LEDblinkState = !LEDblinkState;
        LEDlastMode = mode;
        LEDblinkLast = millis();
    }
  } else if (mode == 3) {
    // slow burst every 1 second
    // Slow 4 blinks (lazy burst) followed by 1 sec pause
    if (LEDlastMode != mode) {
      LEDblinkPWM = 0;
      LEDblinkState = true;
      LEDblinkPeriod = 100;
    }
    if (millis() - LEDblinkLast >= LEDblinkPeriod) {
        if (LEDblinkPWM < 7) {
          if (LEDblinkPWM == 0) LEDblinkState = true;
          digitalWrite(pin, LEDblinkState);
          LEDblinkPeriod = 100;
          LEDblinkState = !LEDblinkState;
          LEDblinkPWM++;
        } else {
          digitalWrite(pin, LOW);
          LEDblinkPWM = 0;
          LEDblinkPeriod = 1000;
        }
        LEDlastMode = mode;
        LEDblinkLast = millis();
    }
  } else if (mode == 4) {
    // fast burst every 1 second
    // Fast 4 blinks (burst) followed by 1 sec pause
    if (LEDlastMode != mode) {
      LEDblinkPWM = 0;
      LEDblinkState = true;
      LEDblinkPeriod = 25;
    }
    if (millis() - LEDblinkLast >= LEDblinkPeriod) {
        if (LEDblinkPWM < 7) {
          if (LEDblinkPWM == 0) LEDblinkState = true;
          digitalWrite(pin, LEDblinkState);
          LEDblinkPeriod = 25;
          LEDblinkState = !LEDblinkState;
          LEDblinkPWM++;
        } else {
          digitalWrite(pin, LOW);
          LEDblinkPWM = 0;
          LEDblinkPeriod = 1000;
        }
        LEDlastMode = mode;
        LEDblinkLast = millis();
    }
  } // mode
}   // blinkLEDnoDelay()


void blinkERR(byte ledPIN){
  // S-O-S
  const uint8_t S = 150, O = 300;
  for(uint8_t i = 3; i>0; i--){
    digitalWrite(ledPIN, HIGH);
    delay(S);
    digitalWrite(ledPIN, LOW);
    delay(S);
  }    
  delay(200);
  for(uint8_t i = 3; i>0; i--){
    digitalWrite(ledPIN, HIGH);
    delay(O);
    digitalWrite(ledPIN, LOW);
    delay(O);
  }    
  delay(200);
  for(uint8_t i = 3; i>0; i--){
    digitalWrite(ledPIN, HIGH);
    delay(S);
    digitalWrite(ledPIN, LOW);
    delay(S);
  }    
  delay(200);
} // blinkERR()


//////////////////////////////////////////////////////////////////////////////
// 

uint8_t button_state = LOW;     // button_state & last_button_state are initialized for a pulldown resistor         
uint8_t last_button_state = LOW;
uint32_t last_debounce_time_ms = 0;  // the last time the output pin was toggled
const uint8_t DEBOUNCE_DELAY_MS = 50;    // The switch debounce time.  50 to 100 ms

void userButton() {
  // Detect a change in the USER_BTN pushbutton state and turn on the
  // USER_BTN in response. 
  
  // This logic is for a button using a pulldown resistor. 
  button_state = digitalRead(USER_BTN); 
  // if millis() or timer wraps around, reset it
  if (last_debounce_time_ms > millis())  last_debounce_time_ms = millis();
  if (button_state != last_button_state) { 
    // Multiple changes in the button_state can occur when a pushbutton is 
    // pressed, or a switch is toggled. Use a debounce timer to only react
    // to a change in button state after an interval of DEBOUNCE_DELAY_MS. 
    last_button_state = button_state;
    //  Check if enough time has passed to evaluate another pushbutton press.
    if ((millis() - last_debounce_time_ms) > DEBOUNCE_DELAY_MS) {
      last_debounce_time_ms = millis();
      if (button_state == LOW) {
          digitalWrite(LED_BUILTIN, HIGH);
      } else {
          digitalWrite(LED_BUILTIN, LOW);
      } // button_state
    } // millis()
  } // button_state != last_button_state    
} // userButton()


//////////////////////////////////////////////////////////////////////////////
// TimerA
// 1000000 us = 1000 ms = 1 sec = 1 Hz
const uint16_t TIMER_A_INTERVAL_MS = 2000; 
uint32_t timer_a_lap_ms = millis();  // timer

void timerA() {
  //  Timer A
  if (timer_a_lap_ms > millis())  timer_a_lap_ms = millis();
  if (millis() - timer_a_lap_ms > TIMER_A_INTERVAL_MS) { 
    // do something here every TIMER_A_INTERVAL_MS / 1000 sec
    Serial.print("USER_BTN: ");
    Serial.println(USER_BTN);

    timer_a_lap_ms = millis(); // reset the timer
  }
} // timerA()

/////////////////////////////////////////////////////////////////////////


void setup() {

  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, LOW);

  pinMode(USER_BTN, INPUT);

  uint16_t serial_timeout = 0;
  Serial.begin(115200);
  while (!Serial && serial_timeout < 50) {
    digitalWrite(LED_BUILTIN, HIGH);
    delay(100);
    digitalWrite(LED_BUILTIN, LOW);
    serial_timeout++;
  }
  Serial.print("\nSerial ready in ");
  Serial.print(serial_timeout);
  Serial.println(" ms");

  Serial.println("Setup finished\n");
} // setup()


void loop() {

  blinkLEDnoDelay(LED_BUILTIN, 1);  // Mode 0 breathing not supported

  userButton();
  
  timerA();

} // loop()

 

Notecard Arduino Library

Communication between a MCU and the Notecard over serial or I2C is accomplished by installing the note-arduino library (GitHub).   In Arduino IDE search for library "Blues Wireless Notecard" by Blues Wireless.  

The Arduino sketch below for a Swan with a WiFi Notecard robustly reconnects to a WiFi access point and posts data to Blynk via a 'web.get'.  


/*
  STM32L4 Feather
  Sold by blues.io (not an Adafruit product)
  
  https://dev.blues.io/quickstart/swan-quickstart/#installing-stm32duino-in-the-arduino-ide

  NOTE: v2.3.0 of STM32 does not compile.
 
 */

// In Arduino IDE, select 'Tools','Board','SM32 boards groups..','Blues Wireless boards'
// And then under 'Board part number' choose 'Swan R5'. 
// Tools -> USB support (if available), select 
// Find USB port in Windows by looking at ..."CDC (generic 'Serial' supersede U(S)ART)."

/////////////////////////////////////////////////////////////////////////
//  note-arduino library  (search for library "Blues Wireless Notecard" by Blues Wireless)

#include <Notecard.h>

Notecard notecard;
#define PRODUCT_UID "com.your-company.your-name:your_product"
#define NOTECARD_SESSION_TOKEN "abcdefghijklmnopQRSTuvWXYZ012345"
#define DEVICE_UID "dev:94abc012345d"
#define WIFI_SSID "YourWiFiAP"
#define WIFI_PSWD "wifiAPpassword"

/////////////////////////////////////////////////////////////////////////
// Built in LED(s) & LiPoly Battery

// Red LED:  LED_BUILTIN is pin #56

// USER_BTN on pin 357 is a programmable button on the Swan
// that is pulled HIGH by default (LOW when pressed).

/////////////////////////////////////////////////////////////////////////
// blinkLEDnoDelay()
unsigned long LEDblinkPeriod = 8;
unsigned long LEDblinkLast = 0;
uint8_t LEDblinkPWM = 0;
bool LEDblinkState = false;
uint8_t LEDlastMode = 0;

void blinkLEDnoDelay(byte pin, byte mode) {
  // Blink the LED on 'pin' without using delay() according to
  // the 'mode' argument defined below. 
  // pin must support PWM. 
  // 
  // mode:
  //  0 = breathing
  //  1 = blink slow constantly
  //  2 = blink fast constantly
  //  3 = slow burst every 1 second
  //  4 = fast burst every 1 second
  //
  // Required global variables: LEDblinkPeriod, LEDblinkLast, LEDblinkPWM, LEDblinkState, LEDlastMode
  if (mode == 0) {
    // breathing
    LEDblinkPeriod = 8;
    if (LEDlastMode != mode) {
      LEDblinkPWM = 0;
      LEDblinkState = true;
      digitalWrite(pin, LOW);
    }
    if (millis() - LEDblinkLast >= LEDblinkPeriod) {
        if (LEDblinkPWM > 254) LEDblinkState = false;
        if (LEDblinkPWM < 1) LEDblinkState = true;
        if (LEDblinkState) {
            LEDblinkPWM++;
        } else {
            LEDblinkPWM--;
        }
        analogWrite(pin, LEDblinkPWM);
        LEDlastMode = mode;
        LEDblinkLast = millis();
    }
  } else if (mode == 1) {
    // blink slow constantly
    LEDblinkPeriod = 1000;
    if (millis() - LEDblinkLast >= LEDblinkPeriod) {
        digitalWrite(pin, LEDblinkState);
        LEDblinkState = !LEDblinkState;
        LEDlastMode = mode;
        LEDblinkLast = millis();
    }
  } else if (mode == 2) {
    // blink fast constantly
    LEDblinkPeriod = 100;
    if (millis() - LEDblinkLast >= LEDblinkPeriod) {
        digitalWrite(pin, LEDblinkState);
        LEDblinkState = !LEDblinkState;
        LEDlastMode = mode;
        LEDblinkLast = millis();
    }
  } else if (mode == 3) {
    // slow burst every 1 second
    // Slow 4 blinks (lazy burst) followed by 1 sec pause
    if (LEDlastMode != mode) {
      LEDblinkPWM = 0;
      LEDblinkState = true;
      LEDblinkPeriod = 100;
    }
    if (millis() - LEDblinkLast >= LEDblinkPeriod) {
        if (LEDblinkPWM < 7) {
          if (LEDblinkPWM == 0) LEDblinkState = true;
          digitalWrite(pin, LEDblinkState);
          LEDblinkPeriod = 100;
          LEDblinkState = !LEDblinkState;
          LEDblinkPWM++;
        } else {
          digitalWrite(pin, LOW);
          LEDblinkPWM = 0;
          LEDblinkPeriod = 1000;
        }
        LEDlastMode = mode;
        LEDblinkLast = millis();
    }
  } else if (mode == 4) {
    // fast burst every 1 second
    // Fast 4 blinks (burst) followed by 1 sec pause
    if (LEDlastMode != mode) {
      LEDblinkPWM = 0;
      LEDblinkState = true;
      LEDblinkPeriod = 25;
    }
    if (millis() - LEDblinkLast >= LEDblinkPeriod) {
        if (LEDblinkPWM < 7) {
          if (LEDblinkPWM == 0) LEDblinkState = true;
          digitalWrite(pin, LEDblinkState);
          LEDblinkPeriod = 25;
          LEDblinkState = !LEDblinkState;
          LEDblinkPWM++;
        } else {
          digitalWrite(pin, LOW);
          LEDblinkPWM = 0;
          LEDblinkPeriod = 1000;
        }
        LEDlastMode = mode;
        LEDblinkLast = millis();
    }
  } // mode
}   // blinkLEDnoDelay()


void blinkERR(byte ledPIN){
  // S-O-S
  const uint8_t S = 150, O = 300;
  for(uint8_t i = 3; i>0; i--){
    digitalWrite(ledPIN, HIGH);
    delay(S);
    digitalWrite(ledPIN, LOW);
    delay(S);
  }    
  delay(200);
  for(uint8_t i = 3; i>0; i--){
    digitalWrite(ledPIN, HIGH);
    delay(O);
    digitalWrite(ledPIN, LOW);
    delay(O);
  }    
  delay(200);
  for(uint8_t i = 3; i>0; i--){
    digitalWrite(ledPIN, HIGH);
    delay(S);
    digitalWrite(ledPIN, LOW);
    delay(S);
  }    
  delay(200);
} // blinkERR()


//////////////////////////////////////////////////////////////////////////////
// 

uint8_t button_state = LOW;     // button_state & last_button_state are initialized for a pulldown resistor         
uint8_t last_button_state = LOW;
uint32_t last_debounce_time_ms = 0;  // the last time the output pin was toggled
const uint8_t DEBOUNCE_DELAY_MS = 50;    // The switch debounce time.  50 to 100 ms

void userButton() {
  // Detect a change in the USER_BTN pushbutton state and turn on the
  // USER_BTN in response. 
  
  // This logic is for a button using a pulldown resistor. 
  button_state = digitalRead(USER_BTN); 
  // if millis() or timer wraps around, reset it
  if (last_debounce_time_ms > millis())  last_debounce_time_ms = millis();
  if (button_state != last_button_state) { 
    // Multiple changes in the button_state can occur when a pushbutton is 
    // pressed, or a switch is toggled. Use a debounce timer to only react
    // to a change in button state after an interval of DEBOUNCE_DELAY_MS. 
    last_button_state = button_state;
    //  Check if enough time has passed to evaluate another pushbutton press.
    if ((millis() - last_debounce_time_ms) > DEBOUNCE_DELAY_MS) {
      last_debounce_time_ms = millis();
      if (button_state == LOW) {
        digitalWrite(LED_BUILTIN, HIGH);
      } else {
        digitalWrite(LED_BUILTIN, LOW);
      } // button_state
    } // millis()
  } // button_state != last_button_state    
} // userButton()


//////////////////////////////////////////////////////////////////////////////


float fGetRandLatitude() {
  // Latitude ranges from -90 to 90 degrees
  uint8_t i = random(0, 90);
  float f = (float)i;
  int sign = random(10);
  if (sign <= 5) {
    f = f * (-1.0);
  }
  return f;
} // fGetLgSignedRandFloat()


float fGetRandLongitude() {
  // Latitude ranges from -180 to +180 degrees
  uint8_t i = random(0, 180);
  float f = (float)i;
  int sign = random(10);
  if (sign <= 5) {
    f = f * (-1.0);
  }
  return f;
} // fGetLgSignedRandFloat()


float fGetSignedRandFloat() {
  long myLong = random(1000000, 3276700);
  long divisor = 100;
  int expnt = random(2,6);
  switch (expnt) {
    case 2:
      divisor = 100;
      break;
    case 3:
      divisor = 1000;
      break;
    case 4:
      divisor = 10000;
      break;
    case 5:
      divisor = 100000;
    default:
      divisor = 100;
  }
  float f = (float)myLong / divisor;
  int sign = random(10);
  if (sign <= 5) {
    f = f * (-1.0);
  }  
  return f;
} // fGetSignedRandFloat()

//////////////////////////////////////////////////////////////////////////////
//  WiFi / Cellular Notecard functions

bool NotecardIsWiFi() {
  // Returns TRUE if the Notecard is a WiFi Notecard.
  // Returns FALSE if the Notecard is a cellular Notecard.

  bool notecard_type_identified = false;
  J *rsp;
  // Wait until the card.status returns a response...
  while (notecard_type_identified == false) {
    rsp = notecard.requestAndResponse(notecard.newRequest("card.status"));
    if (rsp == NULL){
      for(uint8_t i = 0; i<10; i++){
        digitalWrite(LED_BUILTIN, HIGH);
        delay(100);
        digitalWrite(LED_BUILTIN, LOW);
        delay(100);
      }
    } else {
      bool card_type = JGetBool(rsp, "wifi");
      if (card_type == true) return true;

      card_type = JGetBool(rsp, "cell");
      if (card_type == true) return false;  
    }
  }
} // NotecardIsConnected()


bool NotecardIsConnected() {
  // Returns TRUE if the Notecard is connected to the WiFi or cellular network.

  // {"req": "card.wireless"}
  char network_status[20];
  J *rsp = notecard.requestAndResponse(notecard.newRequest("card.wireless"));
  if (rsp == NULL){
    Serial.println("ERROR: card.wireless request returned NULL");
    return false;
  } else {
    char *tempStatus = JGetString(rsp, "status");
    strlcpy(network_status, tempStatus, sizeof(network_status));
    //strncpy(network_status, JGetString(rsp, "status"), sizeof(JGetString(rsp, "status")));
    notecard.deleteResponse(rsp);   
    if (strcmp(network_status,"{network-up}") == 0) {
      return true;
    } else {
      Serial.print("network_status: '"); Serial.print(network_status); Serial.println("'");
      return false;
    }
  }
} // NotecardIsConnected()


bool NotecardNetworkConnect() {
  // Configures the Notecard to be in continuous mode.
  // Performs a hub.sync
  // If the Notecard is a WiFi Notecard, connects to the access point specified by WIFI_SSID 
  // specified by WIFI_SSID using the password from WIFI_PSWD.
  // Returns TRUE if successful.  

  //Serial.println("\nNotecardNetworkConnect()..");

  J *req;
  
  //Serial.println("card.restart ..");
  //req = notecard.newRequest("card.restart");
  //delay(5000);

  if (NotecardIsWiFi() == true) {
    // Switch out of continuous mode to either periodic or minimum before resetting
    // the WiFi credentials.  The Notecard won't break the WiFi connection while in
    // continuous mode.
    req = notecard.newRequest("hub.set");
    if (req) {
      JAddStringToObject(req, "product", PRODUCT_UID);
      JAddStringToObject(req, "mode", "periodic");  // Can also use "minimum"
      if (!notecard.sendRequest(req)) {
          notecard.logDebug("ERROR: hub.set in NotecardNetworkConnect()\n");
          Serial.println("ERROR: hub.set in NotecardNetworkConnect()!\n");
          return false;
      }
    }
    
    // Connect to the WiFi access point using the global variables WIFI_SSID & WIFI_PSWD
    //Serial.println("card.wifi ..");
    req = notecard.newRequest("card.wifi");
    if (req) {
      JAddStringToObject(req, "ssid", WIFI_SSID);
      JAddStringToObject(req, "password", WIFI_PSWD);
      if (!notecard.sendRequest(req)) {
          notecard.logDebug("ERROR: card.wifi in NotecardNetworkConnect()\n");
          Serial.println("ERROR: card.wifi in NotecardNetworkConnect()!\n");
          return false;
      }
    }
  } // NotecardIsWiFi()

  // Make a 'hub.set request and set the 'mode' to 'continuous'..
  // {"req": "hub.set", "product":"com.your-company.your-name:your_product", "mode": "continuous"}
  //Serial.println("\thub.set ..");
  req = notecard.newRequest("hub.set");
  if (req) {
    JAddStringToObject(req, "product", PRODUCT_UID);
    JAddStringToObject(req, "mode", "continuous");
    if (!notecard.sendRequest(req)) {
        notecard.logDebug("ERROR: hub.set in NotecardNetworkConnect()\n");
        Serial.println("ERROR: hub.set in NotecardNetworkConnect()!\n");
        return false;
    }
  }

  // Issue a 'hub.sync' request: {"req":"hub.sync"}
  //Serial.println("\thub.sync ..");
  req = notecard.newRequest("hub.sync");
  if (req) {
    if (!notecard.sendRequest(req)) {
        notecard.logDebug("ERROR: hub.set in NotecardNetworkConnect()\n");
        Serial.println("ERROR: hub.set in NotecardNetworkConnect()!\n");
        return false;
    }
  }

  char network_status[20];
  if (req = notecard.newRequest("card.wireless")) {
    J *rsp = notecard.requestAndResponse(req);
    if (rsp == NULL){
      notecard.logDebug("card.wireless request in CellularNotecardConnect() failed!\n");
      Serial.println("card.wireless request in CellularNotecardConnect() failed!");
      return false;
    } else {
      char *tempStatus = JGetString(rsp, "status");
      strlcpy(network_status, tempStatus, sizeof(network_status));
      notecard.deleteResponse(rsp);   
      if (strcmp(network_status,"{network-up}") == 0) {
        return true;
      } else {
        Serial.print("The Notecard network connection status is '"); Serial.print(network_status); Serial.println("'");
        return false;
      }
    }
  }

  
} // NotecardNetworkConnect


bool NotecardModeIsContinuous() {
  // Returns TRUE if a hub.get reports that the Notecard connection mode is 'continuous'.

  //Serial.println("NotecardModeIsContinuous()..");
  // Execute a {"req":"hub.get"} to get the current settings..
  char mode[20];
  J *rsp = notecard.requestAndResponse(notecard.newRequest("hub.get"));
  if (rsp == NULL){
    Serial.println("hub.get by NotecardModeIsContinuous() failed!");
    blinkERR(LED_BUILTIN);
  } else {
    char *tempMode = JGetString(rsp, "mode");
    strlcpy(mode, tempMode, sizeof(mode));
    notecard.deleteResponse(rsp);  
    if (strcmp(mode,"continuous") == 0) {
      return true;
    } else {
      Serial.print("The Notecard mode is '"); Serial.print(mode); Serial.println("'.");
      return false;
    }
  }
} // NotecardModeIsContinuous()


//////////////////////////////////////////////////////////////////////////////
// TimerA
const uint32_t TIMER_A_INTERVAL_MS = 3600000; // 1 hour = 3600000 ms
uint32_t timer_a_lap_ms = millis();  // timer
uint8_t v1 = 100;
float v0 = 0.0;
float lat = 0.0;
float lon = 0.0;

void timerA() {
  //  Timer A
  if (timer_a_lap_ms > millis())  timer_a_lap_ms = millis();
  if (millis() - timer_a_lap_ms > TIMER_A_INTERVAL_MS) { 
    timer_a_lap_ms = millis(); // reset the timer

    // PRODUCT_UID  NOTECARD_SESSION_TOKEN  DEVICE_UID
    // https://dev.blues.io/tools-and-sdks/libraries/arduino-library/#sending-notecard-requests

    Serial.println("timerA()");

    if (NotecardModeIsContinuous() == false) {
      Serial.println("ERROR: The Notecard mode is not 'continuous'.  Re-connecting to the cellular/WiFi network..");
      if (NotecardNetworkConnect() == false) {
        Serial.println("NotecardNetworkConnect() called from NotecardModeIsContinuous() failed!");
        return;
      }
    }

    digitalWrite(LED_BUILTIN, HIGH);

    // Push data to the proxy route and then to Blynk
    Serial.println("\nMaking a Notecard request web.get..");
    J *req = notecard.newRequest("web.get");
    if (req) {
        Serial.println("web.get init okay");
        JAddStringToObject(req, "route", "BlynkGet");
        JAddStringToObject(req, "name", "&V0=1.23456&V1=112&V2=blues.io&V7=-73.988&V7=40.724");
    
        char c_v1[3];
        char str[25];
        char payload[99];
        sprintf(c_v1, "%u", v1);
        v0 = fGetSignedRandFloat();
        dtostrf(v0, 7, 6, str); 
        strcpy(payload, "&V0=");
        strcat(payload, str);
        strcat(payload, "&V1=");
        strcat(payload, c_v1);
        strcat(payload, "&V2=blues.io&V7=");
        lon = fGetRandLongitude();
        dtostrf(lon, 7, 6, str); 
        strcat(payload, str);
        strcat(payload, "&V7=");
        lat = fGetRandLatitude();
        dtostrf(lat, 7, 6, str); 
        strcat(payload, str);
        Serial.print("payload: '"); Serial.print(payload); Serial.println("'\t");
        JAddStringToObject(req, "name", payload);
        v1++;
        if (v1 > 199) {
          v1 = 100;
        }
        // requestAndResponse() sends a JSON request to the Notecard, and returns
        // the Notecard's JSON response.
        // For this request, a favorable response will be: {"result":200}
        J *rsp = notecard.requestAndResponse(req);
        int result = JGetInt(rsp, "result");
        if (result == 200) {
          Serial.println("Response: 200 okay");
        } else {
          Serial.println("Response: "); Serial.print(result); Serial.println(" ERROR!");
        }
        notecard.deleteResponse(rsp);
    } 

    digitalWrite(LED_BUILTIN, LOW);
    
    timer_a_lap_ms = millis(); // reset the timer
  }
} // timerA()

/////////////////////////////////////////////////////////////////////////


void setup() {
  delay(5000);  // too fast!

  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, LOW);

  pinMode(USER_BTN, INPUT);

  timer_a_lap_ms = 0;
  Serial.begin(115200);
  while (!Serial && timer_a_lap_ms < 50) {
    digitalWrite(LED_BUILTIN, HIGH);
    delay(100);
    digitalWrite(LED_BUILTIN, LOW);
    timer_a_lap_ms++;
  }
  Serial.print("\nSerial ready in ");
  Serial.print(timer_a_lap_ms);
  Serial.println(" ms");
    
  // Initialize Notecard I2C interface (with defaults)
  digitalWrite(LED_BUILTIN, HIGH);
  Serial.println("Initializing Notecard I2C interface..");
  timer_a_lap_ms = millis();
  notecard.begin();
  Serial.print("Notecard I2C interface initialized sucessfully in ");
  Serial.print(millis()-timer_a_lap_ms); Serial.println(" ms");

  Serial.println("Determining the Notecard type..");
  char notecard_type[9] = "";
  if (NotecardIsWiFi() == true) {
    Serial.println("The Notecard type is 'Wifi'");
    strncpy(notecard_type, "WiFi", sizeof(notecard_type));
  } else {
    Serial.println("The Notecard type is 'Cellular'");
    strncpy(notecard_type, "Cellular", sizeof(notecard_type));
  }

  
  // Uncomment below to connect to a connection to a new WiFi access point..
  /*
    if (NotecardIsWiFi() == true) {
    while (NotecardNetworkConnect() == false) {
      Serial.println("NotecardNetworkConnect() called from Setup() failed!");
      for(uint8_t i = 0; i<5; i++){
        blinkERR(LED_BUILTIN);
      }
    } // while
    }
  */

  // Make sure the Notecard is connected to a cellular/WiFi network.  
  // If not, then wait for a connection. 
  // From a cold start it takes 19 s to connect to the WiFi, 36 sec for cellular.
  timer_a_lap_ms = millis();
  if (NotecardIsConnected() == true) {
    Serial.print("The "); Serial.print(notecard_type); Serial.println(" notecard is connected to a network.");
  } else {
    Serial.print("Connecting the "); Serial.print(notecard_type); Serial.println(" Notecard to a network..");
    // do something about it...
    while (NotecardNetworkConnect() == false) {
      for(uint8_t i = 0; i<10; i++){
        digitalWrite(LED_BUILTIN, HIGH);
        delay(100);
        digitalWrite(LED_BUILTIN, LOW);
        delay(100);
      }
      delay(2000);
    } // while
  }
  Serial.print(notecard_type); Serial.print(" Notecard connected in "); Serial.print((millis()-timer_a_lap_ms)/1000); Serial.println(" s");

  digitalWrite(LED_BUILTIN, LOW);
  timer_a_lap_ms = millis();
  Serial.println("\nSetup finished\n");
} // setup()


void loop() {

  blinkLEDnoDelay(LED_BUILTIN, 1);  // Mode 0 breathing not supported

  userButton();
  
  timerA();

} // loop()

 

Quick Links

In-Browser Terminal | Notehub | Notecard docs | Notecarrier-F dimensions | C++ J JSON functions | DFU mode | Notecard Community | Edge Impulse | Hackster.io Hub

 


Do you need help developing or customizing a IoT product for your needs?   Send me an email requesting a free one hour phone / web share consultation.  

 

The information presented on this website is for the author's use only.   Use of this information by anyone other than the author is offered as guidelines and non-professional advice only.   No liability is assumed by the author or this web site.  

Projects

blues.io Solutions

Overview

Getting Started

Blynk