-
Notifications
You must be signed in to change notification settings - Fork 0
/
solar_data_logger.ino
152 lines (132 loc) · 5.42 KB
/
solar_data_logger.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
/* Solar Power Data Logger
https://github.com/Andy4495/solar_data_logger
MIT License
Use MspTandV library to take voltage measurements at regular
intervals and store the data in non-volatile internal FRAM.
Vcc plus a raw voltage are measured. I use the raw voltage
to measure the the solar cell output through a resistor
divider before it goes to the 3.3V voltage regulator.
By storing the data in FRAM, the sketch can be run without
being connected to a computer and the data can be retrieved
at a later time.
Designed for use with MSP430FR2433 powered with a solar panel,
but could be adapted to other boards (particularly other "FR"
variants of the MSP430), and other power sources or data
collection needs.
Holding down PUSH1 at reboot prints the stored data over the
serial port.
Holding down PUSH2 at reboot clears the data store in FRAM.
- THE_LED is briefly flashed when FRAM is cleared.
If a reboot is detected (e.g., due to loss of power from the
solar panel), then 5555 is stored in the next data cell (since
that value is easy to spot and much greater than the ~3300 mV that
can be measured by the MSP430).
THE_LED is flashed every few seconds (per the LED_TIME #define). Since
this takes a little extra power, it may be useful to remove the LED
jumper after confirming that the program is running so that the
measurements aren't affected by the LED current draw.
V_DIV_SCALE_FACTOR is the ratio of the voltage divider used between
the raw voltage and ground. For example if the divider is connected as:
Vraw <---> 10KOhm <---> RAW_ADC_PIN <---> 5KOhm <---> GND
(meaning 10K and 5K divider resistors), then the scaling factor is
5000/(5000 + 10000) = 0.3333.
Uses external library https://github.com/Andy4495/MspTandV
*/
/* Version History
10/19/2023 Andy4495 Original
*/
#include "MspTandV.h"
#define LOOP_TIME 60000UL // Milliseconds between voltage readings
#define LED_TIME 3000UL // Time between LED toggling
#define NUM_SAMPLES (1024 + 512) // 1536 total readings at 60 second intervals stores 25h36m of data (even more since no power at night)
#define REBOOT_DETECTED 5555
#define V_DIV_SCALE_FACTOR 0.3197 // We are going to use floating point math for this, since we have the space
#define RAW_ADC_PIN 5 // Analog pin used to measure the raw voltage (typically through a voltage divider)
#define LED_PWM_LEVEL 32 // Controls LED brightness, higher is brighter (and uses more current)
#if defined(__MSP430FR6989__) // The FR6989 does not define LED2, so use GREEN_LED instead
#define THE_LED GREEN_LED
#else
#define THE_LED LED2
#endif
MspVcc myVcc;
MspAdc myAdc(RAW_ADC_PIN, 1); // Using internal voltage reference 1, which is 1.5V on FR2433. See MspTandV library if using a different processor.
uint16_t first_time PLACE_IN_FRAM;
uint16_t loop_counter PLACE_IN_FRAM;
uint16_t vcc[NUM_SAMPLES] PLACE_IN_FRAM;
uint16_t adc[NUM_SAMPLES] PLACE_IN_FRAM;
unsigned long prev_millis = 0;
unsigned long led_millis = 0;
unsigned long current_millis;
float divided_mv;
int led_state = 0;
void setup() {
Serial.begin(9600);
#if defined(__MSP430FR2433__)
SYSCFG0 = FRWPPW; // Write enable data and program FRAM
#endif
pinMode(THE_LED, OUTPUT);
digitalWrite(THE_LED, LOW);
pinMode(PUSH2, INPUT_PULLUP);
// Check if this is the first time through
// Either look for a "magic number" (0x49) or PUSH2 pressed at reset
if ((first_time != 0x49) || (digitalRead(PUSH2) == LOW)) {
Serial.println("Clearing FRAM, start logging at 0.");
for (int i = 0; i < NUM_SAMPLES; i++) {
vcc[i] = 0;
adc[i] = 0;
}
loop_counter = 0;
first_time = 0x49;
// Briefly flash LED to indicate memory clear
digitalWrite(THE_LED, HIGH);
delay(500);
digitalWrite(THE_LED, LOW);
} else { // If there was a reboot, put the REBOOT_DETECTED value in the next slot
vcc[loop_counter++] = REBOOT_DETECTED;
if (loop_counter >= NUM_SAMPLES) loop_counter = 0;
Serial.print("Reboot detected. start logging at: ");
Serial.println(loop_counter);
}
pinMode(PUSH1, INPUT_PULLUP);
if (digitalRead(PUSH1) == LOW) {
for (int i = 0; i < NUM_SAMPLES; i++) {
Serial.print("i, vcc[i], adc[i]: ");
Serial.print(i);
Serial.print(", ");
Serial.print(vcc[i]);
Serial.print(", ");
Serial.print(adc[i]);
Serial.println("");
}
}
}
void loop() {
current_millis = millis();
if (current_millis - prev_millis > LOOP_TIME) {
prev_millis = millis();
myVcc.read(CAL_ONLY);
vcc[loop_counter] = myVcc.getVccCalibrated();
Serial.print("Loop, Vcc, ADC_Calibrated, V_Calculated: ");
Serial.print(loop_counter);
Serial.print(" , ");
myVcc.read(CAL_ONLY);
Serial.print(vcc[loop_counter]);
Serial.print(", ");
myAdc.read();
adc[loop_counter] = myAdc.getAdcCalibrated();
Serial.print(adc[loop_counter]);
Serial.print(", ");
divided_mv = adc[loop_counter] / (float)ADC_STEPS * (float)VCC_REF1_DV / V_DIV_SCALE_FACTOR / 10.0;
Serial.print(divided_mv, 6);
Serial.println("");
loop_counter++;
if (loop_counter >= NUM_SAMPLES) loop_counter = 0;
}
// Flash the LED periodically, but low power using PWM
if (current_millis - led_millis > LED_TIME) {
led_millis = current_millis;
if (led_state == 0) analogWrite(THE_LED, LED_PWM_LEVEL);
else analogWrite(THE_LED, 0);
led_state = ~led_state;
}
}