forked from chrisb2/battery-monitor
-
Notifications
You must be signed in to change notification settings - Fork 0
/
battery-monitor.ino
161 lines (133 loc) · 3.95 KB
/
battery-monitor.ino
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
#define VCC 3273
#define BATTERY_PIN A0
#define R1 221200 // Resistor R1 Ohms
#define R2 21440 // Resistor R2 Ohms
#define CONNECT_WAIT_MILLIS 10000
#define LOW_THRESHOLD 12000 // milliVolts
#define ADC_SAMPLES 20
#define ADC_DISPERSED 2
#define SLEEP_SECS 3600 // 1 hour
//#define SLEEP_SECS 15
// Set manual mode so that if no network is available, photon can go back to sleep
SYSTEM_MODE(MANUAL);
const float DIV_RATIO = (R1 + R2) / (float) R2;
const char VOLTAGE_EVENT[11] = "carVoltage";
const char LOW_EVENT[14] = "carLowVoltage";
const char FLASH_REQUEST[13] = "flashRequest";
const char FLASH_READY[13] = "flashReady";
// set to true when the photon can sleep
volatile bool canSleep = false;
// set to true when photon is waiting for OTA update
volatile bool flashing = false;
void setup() {
disableLed();
// Measure voltage before connecting to avoid ADC noise due to network activity
uint16_t batteryMillivolts = getCentralAvgBatteryMilliVolts();
Particle.connect();
if (waitFor(Particle.connected, CONNECT_WAIT_MILLIS)) {
subscribeToFlashEvent();
subscribeToVoltageEvent();
publishVoltageEvent(batteryMillivolts);
// Listen for voltage event to make sure it was published before sleeping
waitForVoltageEvent();
waitForFlashEvent();
}
if (!flashing) {
System.sleep(SLEEP_MODE_DEEP, SLEEP_SECS);
}
}
void loop() {
signalReadyForFlash();
if (flashing) {
publishFlashReadyEvent();
flashing = false;
}
}
void signalReadyForFlash() {
RGB.color(0, 255, 0);
delay(500);
RGB.brightness(5);
delay(50);
RGB.brightness(0);
Particle.process();
}
void subscribeToVoltageEvent() {
Particle.subscribe(VOLTAGE_EVENT, subscribeHandler, MY_DEVICES);
}
void subscribeToFlashEvent() {
Particle.subscribe(FLASH_REQUEST, flashEventHandler);
}
void publishFlashReadyEvent() {
Particle.publish(FLASH_READY, PRIVATE);
}
void publishVoltageEvent(uint16_t milliVolts) {
char eventData[5];
sprintf(eventData, "%.1f", milliVolts / 1000.0);
if (milliVolts < LOW_THRESHOLD) {
Particle.publish(LOW_EVENT, eventData, 60, PRIVATE);
}
Particle.publish(VOLTAGE_EVENT, eventData, 60, PRIVATE);
}
void waitForVoltageEvent() {
int waitCount = 0;
while (!canSleep && waitCount++ < 100) {
delay(100);
}
}
void waitForFlashEvent() {
int waitCount = 0;
while (!flashing && waitCount++ < 50) {
delay(100);
}
}
void disableLed() {
RGB.control(true);
RGB.brightness(0);
}
void subscribeHandler(const char* event, const char* data) {
canSleep = true;
}
void flashEventHandler(const char* event, const char* data) {
flashing = true;
}
// Averaging of N-X ADC samples as per ST Application Note AN4073
uint16_t getCentralAvgBatteryMilliVolts() {
uint32_t mV = 0;
uint16_t readings[ADC_SAMPLES];
for (int i = 0; i < ADC_SAMPLES; i++) {
readings[i] = readMilliVolts(BATTERY_PIN);
delay(3);
}
qsort(readings, ADC_SAMPLES, sizeof(uint16_t), uint16Compare);
for (int i = ADC_DISPERSED; i < (ADC_SAMPLES - ADC_DISPERSED); i++) {
mV += readings[i];
}
return round(mV * DIV_RATIO / (float) (ADC_SAMPLES - (2 * ADC_DISPERSED)));
}
// Averaging of N ADC samples as per ST Application Note AN4073
uint16_t getAvgBatteryMilliVolts() {
uint32_t mV = 0;
for (int i = 0; i < ADC_SAMPLES; i++) {
mV += readMilliVolts(BATTERY_PIN);
delay(3);
}
return round(mV * DIV_RATIO / (float) ADC_SAMPLES);
}
uint16_t getBatteryMilliVolts() {
uint16_t mV = readMilliVolts(BATTERY_PIN);
return round(mV * DIV_RATIO);
}
uint16_t readMilliVolts(int pin) {
return map(analogRead(pin), 0, 4095, 0, VCC);
}
int uint16Compare (const void *pa, const void *pb) {
uint16_t a = *(const uint16_t *) pa;
uint16_t b = *(const uint16_t *) pb;
if (a < b) {
return -1;
} else if (a > b) {
return 1;
} else {
return 0;
}
}