|
| 1 | +// |
| 2 | +// Copyright 2022 Blues Inc. All rights reserved. |
| 3 | +// Use of this source code is governed by licenses granted by the |
| 4 | +// copyright holder including that found in the LICENSE file. |
| 5 | +// |
| 6 | +// This example contains the complete source for the Sensor Tutorial at dev.blues.io |
| 7 | +// https://dev.blues.io/build/tutorials/sensor-tutorial/notecarrier-af/esp32/arduino-wiring/ |
| 8 | +// |
| 9 | +// This tutorial requires a Notecarrier-F (or equivalently-wired carrier board) designed |
| 10 | +// enable the Notecard's ATTN pin to control a host MCU's power supply. |
| 11 | +// |
| 12 | + |
| 13 | +#include <Notecard.h> |
| 14 | +#include <Wire.h> |
| 15 | + |
| 16 | +// Parameters for this example |
| 17 | +#define notehubProductUID "com.your_company.your_name:your_project" // if you're your_name@your_company.com |
| 18 | +#define notehubUploadPeriodMins 10 |
| 19 | +#define hostSleepSeconds 60 |
| 20 | + |
| 21 | +// Arduino serial debug monitor port definitions |
| 22 | +#define serialDebug Serial |
| 23 | + |
| 24 | +// Notecard I2C port definitions |
| 25 | +Notecard notecard; |
| 26 | + |
| 27 | +// When the Notecard puts the host MCU to sleep, it enables the host to save 'state' inside the |
| 28 | +// notecard while it's asleep, and to retrieve this state when it awakens. These are several |
| 29 | +// 'segments' of state that may individually be saved. |
| 30 | +struct { |
| 31 | + int cycles; |
| 32 | +} globalState; |
| 33 | +const char globalSegmentID[] = "GLOB"; |
| 34 | + |
| 35 | +struct { |
| 36 | + int measurements; |
| 37 | +} tempSensorState; |
| 38 | +const char tempSensorSegmentID[] = "TEMP"; |
| 39 | + |
| 40 | +struct { |
| 41 | + int measurements; |
| 42 | +} voltSensorState; |
| 43 | +const char voltSensorSegmentID[] = "VOLT"; |
| 44 | + |
| 45 | +// One-time Arduino initialization |
| 46 | +void setup() |
| 47 | +{ |
| 48 | + |
| 49 | + // Arduino IDE requires a delay to move the serial port over |
| 50 | + // from programming the MCU to the debug monitor. |
| 51 | + delay(2500); |
| 52 | + serialDebug.begin(115200); |
| 53 | + notecard.setDebugOutputStream(serialDebug); |
| 54 | + |
| 55 | + // Initialize the physical I2C I/O channel to the Notecard |
| 56 | + Wire.begin(); |
| 57 | + notecard.begin(); |
| 58 | + |
| 59 | + // Determine whether or not this is a 'clean boot', or if we're |
| 60 | + // restarting after having been put to sleep by the Notecard. |
| 61 | + NotePayloadDesc payload; |
| 62 | + bool retrieved = NotePayloadRetrieveAfterSleep(&payload); |
| 63 | + |
| 64 | + // If the payload was successfully retrieved, attempt to restore state from the payload |
| 65 | + if (retrieved) { |
| 66 | + |
| 67 | + // Restore the various state data structures |
| 68 | + retrieved &= NotePayloadGetSegment(&payload, globalSegmentID, &globalState, sizeof(globalState)); |
| 69 | + retrieved &= NotePayloadGetSegment(&payload, tempSensorSegmentID, &tempSensorState, sizeof(tempSensorState)); |
| 70 | + retrieved &= NotePayloadGetSegment(&payload, voltSensorSegmentID, &voltSensorState, sizeof(voltSensorState)); |
| 71 | + |
| 72 | + // We're done with the payload, so we can free it |
| 73 | + NotePayloadFree(&payload); |
| 74 | + |
| 75 | + } |
| 76 | + |
| 77 | + // If this is our first time through, initialize the Notecard and state |
| 78 | + if (!retrieved) { |
| 79 | + |
| 80 | + // Initialize operating state |
| 81 | + memset(&globalState, 0, sizeof(globalState)); |
| 82 | + memset(&tempSensorState, 0, sizeof(tempSensorState)); |
| 83 | + memset(&voltSensorState, 0, sizeof(voltSensorState)); |
| 84 | + |
| 85 | + // Initialize the Notecard |
| 86 | + J *req = NoteNewRequest("hub.set"); |
| 87 | + JAddStringToObject(req, "product", notehubProductUID); |
| 88 | + JAddStringToObject(req, "mode", "periodic"); |
| 89 | + JAddNumberToObject(req, "outbound", notehubUploadPeriodMins); |
| 90 | + NoteRequest(req); |
| 91 | + |
| 92 | + // Because many devs will be using oscilloscopes or joulescopes to closely examine power |
| 93 | + // consumption, it can be helpful during development to provide a stable and repeatable |
| 94 | + // power consumption environment. In the Notecard's default configuration, the |
| 95 | + // accelerometer is 'on'. As such, when debugging, devs may see tiny little blips from |
| 96 | + // time to time on the scope. These little blips are caused by accelerometer interrupt |
| 97 | + // processing, when developers accidentally tap the notecard or carrier. As such, |
| 98 | + // to help during development and measurement, this request disables the accelerometer. |
| 99 | + req = NoteNewRequest("card.motion.mode"); |
| 100 | + JAddBoolToObject(req, "stop", true); |
| 101 | + NoteRequest(req); |
| 102 | + |
| 103 | + } |
| 104 | + |
| 105 | +} |
| 106 | + |
| 107 | +void loop() |
| 108 | +{ |
| 109 | + |
| 110 | + // Bump the number of cycles |
| 111 | + globalState.cycles++; |
| 112 | + |
| 113 | + // Simulation of a device taking a measurement of a temperature sensor. Because we |
| 114 | + // don't have an actual external hardware sensor in this example, we're just retrieving |
| 115 | + // the internal surface temperature of the Notecard. |
| 116 | + double currentTemperature = 0.0; |
| 117 | + J *rsp = NoteRequestResponse(NoteNewRequest("card.temp")); |
| 118 | + if (rsp != NULL) { |
| 119 | + currentTemperature = JGetNumber(rsp, "value"); |
| 120 | + NoteDeleteResponse(rsp); |
| 121 | + tempSensorState.measurements++; |
| 122 | + } |
| 123 | + |
| 124 | + // Simulation of a device taking a measurement of a voltage sensor. Because we |
| 125 | + // don't have an actual external hardware sensor in this example, we're just retrieving |
| 126 | + // the battery voltage being supplied to the Notecard. |
| 127 | + double currentVoltage = 0.0; |
| 128 | + rsp = NoteRequestResponse(NoteNewRequest("card.voltage")); |
| 129 | + if (rsp != NULL) { |
| 130 | + currentVoltage = JGetNumber(rsp, "value"); |
| 131 | + NoteDeleteResponse(rsp); |
| 132 | + voltSensorState.measurements++; |
| 133 | + } |
| 134 | + |
| 135 | + // Add a note to the Notecard containing the sensor readings |
| 136 | + J *req = NoteNewRequest("note.add"); |
| 137 | + if (req != NULL) { |
| 138 | + JAddStringToObject(req, "file", "example.qo"); |
| 139 | + J *body = JCreateObject(); |
| 140 | + if (body != NULL) { |
| 141 | + JAddNumberToObject(body, "cycles", globalState.cycles); |
| 142 | + JAddNumberToObject(body, "temperature", currentTemperature); |
| 143 | + JAddNumberToObject(body, "temperature_measurements", tempSensorState.measurements); |
| 144 | + JAddNumberToObject(body, "voltage", currentVoltage); |
| 145 | + JAddNumberToObject(body, "voltage_measurements", voltSensorState.measurements); |
| 146 | + JAddItemToObject(req, "body", body); |
| 147 | + } |
| 148 | + NoteRequest(req); |
| 149 | + } |
| 150 | + |
| 151 | + // Put ourselves back to sleep for a fixed period of time |
| 152 | + NotePayloadDesc payload = {0, 0, 0}; |
| 153 | + NotePayloadAddSegment(&payload, globalSegmentID, &globalState, sizeof(globalState)); |
| 154 | + NotePayloadAddSegment(&payload, voltSensorSegmentID, &voltSensorState, sizeof(voltSensorState)); |
| 155 | + NotePayloadAddSegment(&payload, tempSensorSegmentID, &tempSensorState, sizeof(tempSensorState)); |
| 156 | + NotePayloadSaveAndSleep(&payload, hostSleepSeconds, NULL); |
| 157 | + |
| 158 | + // We should never return here, because the Notecard put us to sleep. If we do |
| 159 | + // get here, it's because the Notecarrier was configured to supply power to this |
| 160 | + // host MCU without being switched by the ATTN pin. |
| 161 | + delay(15000); |
| 162 | + |
| 163 | +} |
0 commit comments