-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathnode.ino
More file actions
312 lines (268 loc) · 7.76 KB
/
node.ino
File metadata and controls
312 lines (268 loc) · 7.76 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
#include <Loom_Manager.h> //4.6
#include <Hardware/Loom_Hypnos/Loom_Hypnos.h>
#include <Hardware/Actuators/Loom_Neopixel/Loom_Neopixel.h>
#include <Sensors/Loom_Analog/Loom_Analog.h>
#include <Sensors/Analog/Loom_Teros10/Loom_Teros10.h>
#include <Sensors/I2C/Loom_SHT31/Loom_SHT31.h>
#include <Radio/Loom_LoRa/Loom_LoRa.h>
#include "AS5311.h"
//////////////////////////
/* DEVICE CONFIGURATION */
//////////////////////////
static const uint8_t NODE_NUMBER = 1;
static const char * DEVICE_NAME = "HazelnutDendrometer_";
////Select one wireless communication option
#define DENDROMETER_LORA
TimeSpan sleepInterval;
static const uint8_t TRANSMIT_INTERVAL = 16; // to save power, only transmit every X measurements
////Use teros 10?
//#define DENDROMETER_TEROS10
//////////////////////////
//////////////////////////
// Pins
#define AS5311_CS A3 // 9 for LB version, A3 otherwise
#define AS5311_CLK A5
#define AS5311_DO A4
#define BUTTON_PIN A1 // 11 for LB, A1 otherwise
// Loom
Manager manager(DEVICE_NAME, NODE_NUMBER);
Loom_Hypnos hypnos(manager, HYPNOS_VERSION::V3_3, TIME_ZONE::PST); // 3_2 for LB, 3_3 otherwise
// Loom Sensors
Loom_Analog analog(manager);
#ifdef DENDROMETER_TEROS10
Loom_Teros10 teros(manager, A0);
#endif
Loom_SHT31 sht(manager);
Loom_Neopixel statusLight(manager, false, false, true, NEO_GRB); // using channel 2 (physical pin A2). use RGB for through-hole LED devices. GRB otherwise.
// magnet sensor
AS5311 magnetSensor(AS5311_CS, AS5311_CLK, AS5311_DO);
// wireless
#if defined DENDROMETER_LORA
Loom_LoRa lora(manager, NODE_NUMBER);
Loom_BatchSD batchSD(hypnos, TRANSMIT_INTERVAL);
#else
#warning Wireless communication disabled!
#endif
// Global Variables
volatile bool buttonPressed = false; // Check to see if button was pressed
void sleepCycle();
void ISR_RTC();
void ISR_BUTTON();
void measure();
void measureVPD();
void transmit();
void setRTC(bool);
void checkMagnetSensor();
void alignMagnetSensor();
bool checkStableAlignment();
void displayMagnetStatus(magnetStatus);
void flashColor(uint8_t r, uint8_t g, uint8_t b);
/**
* Program setup
*/
void setup()
{
pinMode(BUTTON_PIN, INPUT_PULLUP); // Enable pullup on button pin. this is necessary for the interrupt (and the button check on the next line)
delay(10);
bool userInput = !digitalRead(BUTTON_PIN); // wait for serial connection ONLY button is pressed (low reading)
manager.beginSerial(userInput); // wait for serial connection ONLY button is pressed
hypnos.setLogName("HazelnutDendrometer_1data"); //SD card CSV file name
hypnos.enable();
sleepInterval = hypnos.getConfigFromSD("HypnosConfig.json");
#if defined DENDROMETER_LORA
lora.setBatchSD(batchSD);
#endif
manager.initialize();
setRTC(userInput);
checkMagnetSensor();
alignMagnetSensor();
hypnos.registerInterrupt(ISR_RTC);
}
/**
* Main loop
*/
void loop()
{
measure();
if (buttonPressed) // if interrupt button was pressed, display staus of magnet sensor
{
displayMagnetStatus(magnetSensor.getMagnetStatus());
delay(3000);
statusLight.set_color(2, 0, 0, 0, 0); // LED Off
buttonPressed = false;
}
transmit();
sleepCycle();
}
/**
* Perform all measurements for the dendrometer and put them into a packet.
* Log to SD card.
*/
void measure()
{
manager.measure();
manager.package();
measureVPD();
magnetSensor.measure(manager);
// Log whether system woke up from button or not
manager.addData("Button", "Pressed?", buttonPressed);
hypnos.logToSD();
// delay(5000);
// manager.display_data();
}
/**
* Log readings from the SHT31 sensor. Also calculate and log VPD.
*/
void measureVPD()
{
float SVP, VPD, temperature, humidity;
float e = 2.71828;
temperature = sht.getTemperature();
humidity = sht.getHumidity();
// Tetens equation
SVP = (0.61078 * pow(e, (17.2694 * temperature) / (temperature + 237.3)));
VPD = SVP * (1 - (humidity / 100));
manager.addData("SHT31", "VPD", VPD);
}
/**
* transmit the batch data packet over LoRa
*/
void transmit()
{
#if defined DENDROMETER_LORA
lora.sendBatch(0);
#endif
}
/**
* Shut down the device for a specified time period to save power.
*/
void sleepCycle()
{
hypnos.setInterruptDuration(sleepInterval);
// Reattach to the interrupt after we have set the alarm so we can have repeat triggers
hypnos.reattachRTCInterrupt();
attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), ISR_BUTTON, FALLING);
// Put the device into a deep sleep, operation HALTS here until the interrupt is triggered
hypnos.sleep();
detachInterrupt(digitalPinToInterrupt(BUTTON_PIN));
}
// Interrupt routines
void ISR_RTC()
{
hypnos.wakeup();
}
void ISR_BUTTON()
{
buttonPressed = true;
hypnos.wakeup();
}
/**
* Magnet alignment procedure. Displays magnet sensor to user until
* the magnet is determined to be properly aligned and maintains that alignment
* for a certain amount of time
*/
void alignMagnetSensor()
{
magnetStatus status;
Serial.println(F("<Dendrometer> Waiting for magnet alignment"));
while (1)
{
// Watchdog.reset();
status = magnetSensor.getMagnetStatus();
displayMagnetStatus(status);
delay(100);
if (status == magnetStatus::green && checkStableAlignment())
break;
}
flashColor(0, 255, 0);
}
/**
* Check the magnet sensor alignment status and display it on the multi-color LED
* @param status the magnetStatus to display
*/
void displayMagnetStatus(magnetStatus status)
{
switch (status)
{
case magnetStatus::yellow:
statusLight.set_color(2, 0, 255, 100, 0); // yellow
break;
case magnetStatus::green:
statusLight.set_color(2, 0, 0, 255, 0); // green
break;
case magnetStatus::error: // Fall through
case magnetStatus::red: // Fall through
default:
statusLight.set_color(2, 0, 255, 0, 0); // red
break; // do nothing
}
}
/**
* Flashes status light
* @param r red color value (unsigned 8 bit number)
* @param g green color value (unsigned 8 bit number)
* @param b blue color value (unsigned 8 bit number)
*/
void flashColor(uint8_t r, uint8_t g, uint8_t b)
{
for (auto _ = 6; _--;)
{
// Watchdog.reset();
statusLight.set_color(2, 0, r, g, b);
delay(250);
statusLight.set_color(2, 0, 0, 0, 0); // off
delay(250);
}
}
/**
* Make sure calibration is stable before proceeding
* Returns true if the sensor remains aligned for the next five seconds
*/
bool checkStableAlignment()
{
const unsigned int CHECK_TIME = 3000;
magnetStatus status;
bool aligned = true;
for (int i = 0; i < (CHECK_TIME / 100); i++)
{
// Watchdog.reset();
status = magnetSensor.getMagnetStatus();
if (status != magnetStatus::green)
{
aligned = false;
break;
}
delay(100);
}
return aligned;
}
/**
* Checks to see if a magnet sensor is connected and functioning.
*/
void checkMagnetSensor()
{
uint32_t data = magnetSensor.getRawData();
if (__builtin_parity(data) == 0 && data != 0) //__builtin_parity() returns 0 if value has even parity
return;
for (auto _ = 6; _--;)
flashColor(255, 100, 0); // if the check didn't pass, alert the user by flashing the LED
}
/**
* Ask the user to set a custom time
*/
void setRTC(bool wait)
{
if (!wait || !Serial)
return;
Serial.println(F("<Dendrometer> Adjust RTC time? (y/n)"));
while (!Serial.available())
;
int val = Serial.read();
delay(50);
while (Serial.available())
Serial.read(); // flush the input buffer to avoid invalid input to rtc function
if (val == 'y')
{
hypnos.set_custom_time();
}
}