DIY Logging Volt/Ampmeter
main.c File Reference

Main source-file for the high-precision logging voltage/current meter project. More...

#include "main.h"
#include "adc.h"
#include "i2c.h"
#include "tim.h"
#include "gpio.h"
#include "usart.h"
#include "SEGGER_RTT.h"
#include "util.h"
#include "conversion.h"
#include "display.h"
#include "rtc.h"

Go to the source code of this file.

Macros

#define MAX_WHILELOOPS_METER_RESPONSE   9000
 
#define SHUTDOWN_DELAY_UART_MODULE_MS   500
 
#define STARTUP_DELAY_UART_MODULE_MS   2000
 
#define VBAT_MEASUREMENT_PERIOD_S   10
 

Functions

void SystemClock_Config (void)
 System Clock Configuration. More...
 
void writeHeader (void)
 Function to print the header of the logfile to indicate the columns. More...
 
void writeFooter (void)
 Function to print the footer of the logfile (two lines). More...
 
void writeMeasurements (void)
 Function to print measurement data. More...
 
void checkEncoder (void)
 Function to handle encoder rotation logic. More...
 
void checkSwitches (void)
 Function to handle switch presses. More...
 
int main (void)
 The application entry point. More...
 
void Error_Handler (void)
 This function is executed in case of error occurrence. More...
 

Variables

static const uint8_t aBY56W_SingleRead [4] = {0x88, 0xAE, 0x00, 0x11}
 
struct Data_t data
 
struct cData_t cData
 
struct Settings_t settings
 
struct RTC_dateTime_t rtc
 
static const uint16_t LUT_period_s [16] = {0, 1, 2, 5, 10, 15, 30, 60, 120, 300, 600, 900, 1800, 3600, 7200, 18000}
 
static const uint8_t LUT_period_s_length = 16
 

Detailed Description

Main source-file for the high-precision logging voltage/current meter project.

The code in this project asks two BY56W volt/current meters for measurements, calculates a few additional values (min/max/power/Ah/Wh) with these, prints them to displays and sends them (with time/date values) to a UART port so a Sparkfun OpenLog can log these measurements to a .TXT file, which can eventually be renamed to a .CSV so the measurements can be easily plotted.

Version
1.2
Author
Brecht Van Eeckhoudt

Versions

  • v1.0: Initial version.
  • v1.1: Started printing date before time for each measurement-line, removed day-writing in footer.
  • v1.2: Compared power floats more reliably.

Todo:
Things to do in the near future:
  • Check Ah/Wh calculations.
  • Check if the power/Ah/Wh calculations are done using wrong/old measurements.
  • Handle uint32_t wraparound for time between calculations measurements and waiting time.
  • Don't send/do certain functions if one of the meters is powered down.
Todo:
Long-term future improvements:
  • Add function to stop measurements when the voltage/current is (above/) below a certain value.
  • Add external temperature reading functionality.
  • Add internal temperature reading functionality (MX_ADC1_Init();).
  • Add interrupt-logging functionality.
  • Add relay-opening functionality.
  • Add "manual measurement-mode" to log on up-button press.
  • Add "performance-mode" with no display refreshes.
    • (sub-page on right display, start/stop with up, beeps for confirmation)
  • Add low-battery beeping.
  • Add buzzer functionality using TIM2 (MX_TIM2_Init();).
    • (short beep button-press, longer when measurement taken, add ON/OFF settings page)
  • Optimize encoder logic so there is less copy-pasting.
  • Add debounce logic to getSW0();, getSW1(); and getENCkey();.
  • Use LL_GetTick(); instead of HAL_GetTick();?
  • Add circular pages for setting day/month/hour/minute/second (changeDateTime).
  • Add day-checking in function of the month?
  • Use defines for circular pages?
  • Start using aBY56W_ContinuousRead? Send on every meter power-up?
  • Only one while-loop (USART_HandleContinuousReception) when one meter is connected, way more if both are? Optimize this?
  • Add overflow catch if data.voltage/current are negative values?
    • data.power > 999.999, data.ah > 9999.999, data.wh > 9999.999
  • Remove all Segger-RTT functionality?
  • Store settings in power-down-safe memory.

License

Copyright (C) 2021 - Brecht Van Eeckhoudt

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

A copy of the GNU General Public License can be found in the LICENSE file along with this source code.


Some methods also use code obtained from examples from STMicroelectronics.

Copyright (c) 2020 STMicroelectronics. All rights reserved.

These sections are licensed by ST under BSD 3-Clause license, the "License"; One may not use these example files except in compliance with the License. One may obtain a copy of the License at: opensource.org/licenses/BSD-3-Clause

Definition in file main.c.

Macro Definition Documentation

◆ MAX_WHILELOOPS_METER_RESPONSE

#define MAX_WHILELOOPS_METER_RESPONSE   9000

Max amount of while loop passes before exiting the while loop early (~meter unresponsive)

Definition at line 119 of file main.c.

◆ SHUTDOWN_DELAY_UART_MODULE_MS

#define SHUTDOWN_DELAY_UART_MODULE_MS   500

Waiting time before shutting down the external UART logger after writing the footer

Definition at line 122 of file main.c.

◆ STARTUP_DELAY_UART_MODULE_MS

#define STARTUP_DELAY_UART_MODULE_MS   2000

Waiting time for an external UART power-up message before writing the header

Definition at line 125 of file main.c.

◆ VBAT_MEASUREMENT_PERIOD_S

#define VBAT_MEASUREMENT_PERIOD_S   10

Time between battery voltage measurements

Definition at line 128 of file main.c.

Function Documentation

◆ checkEncoder()

void checkEncoder ( void  )

Function to handle encoder rotation logic.

Definition at line 656 of file main.c.

657 {
658  int16_t steps = 0;
659  uint16_t encValue = LL_TIM_GetCounter(TIM3);
660 
661  if (encValue != settings.oldTIM3)
662  {
663  /* Make sure we get a stable value */
664  while ((encValue % 4) > 0) encValue++;
665 
666  /* Calculate steps */
667  steps = encValue - settings.oldTIM3;
668  steps /= 4;
669 
670  /* Logic for circular pages */
671  if (steps > 0)
672  {
673  for (uint8_t counter = 0; counter < steps; counter++)
674  {
675  /* Rotate LEFT display pages (if selected) */
676  if (settings.selectedDisplay == 0)
677  {
678  if (settings.leftPage < 2) settings.leftPage++;
679  else settings.leftPage = 0;
680 
681  /* Indicate that a certain page needs to be updated */
682  if (settings.leftPage == 0)
683  {
684  cData.newPow = 1;
685  cData.newMaxVolt = 1;
686  cData.newMinVolt = 1;
687  }
688  else if (settings.leftPage == 1)
689  {
690  cData.newPow = 1;
691  cData.newMaxCurr = 1;
692  cData.newMinCurr = 1;
693  }
694  else if (settings.leftPage == 2)
695  {
696  cData.newPow = 1;
697  cData.newAh = 1;
698  cData.newWh = 1;
699  }
700  }
701 
702  /* Rotate RIGHT display pages (if selected) */
703  if (settings.selectedDisplay == 1)
704  {
705  /* Change period (if selected) */
706  if (settings.changePeriod == 1)
707  {
709  {
714  }
715  }
716  /* Change day (if selected) */
717  else if (settings.changeDateTime == 1)
718  {
719  if ((rtc.latest == 0) && (rtc.day0 < 31)) rtc.day0++;
720  else if ((rtc.latest == 1) && (rtc.day1 < 31)) rtc.day1++;
721 
723  }
724  /* Change month (if selected) */
725  else if (settings.changeDateTime == 2)
726  {
727  if ((rtc.latest == 0) && (rtc.month0 < 12)) rtc.month0++;
728  else if ((rtc.latest == 1) && (rtc.month1 < 12)) rtc.month1++;
729 
731  }
732  /* Change year (if selected) */
733  else if (settings.changeDateTime == 3)
734  {
735  if (rtc.latest == 0) rtc.year0++;
736  else if (rtc.latest == 1) rtc.year1++;
737 
739  }
740  /* Change hour (if selected) */
741  else if (settings.changeDateTime == 4)
742  {
743  if ((rtc.latest == 0) && (rtc.hour0 < 23)) rtc.hour0++;
744  else if ((rtc.latest == 1) && (rtc.hour1 < 23)) rtc.hour1++;
745 
747  }
748  /* Change minutes (if selected) */
749  else if (settings.changeDateTime == 5)
750  {
751  if ((rtc.latest == 0) && (rtc.min0 < 59)) rtc.min0++;
752  else if ((rtc.latest == 1) && (rtc.min1 < 59)) rtc.min1++;
753 
755  }
756  /* Change seconds (if selected) */
757  else if (settings.changeDateTime == 6)
758  {
759  if ((rtc.latest == 0) && (rtc.sec0 < 59)) rtc.sec0++;
760  else if ((rtc.latest == 1) && (rtc.sec1 < 59)) rtc.sec1++;
761 
763  }
764  else
765  {
766  /* Rotate right display pages (if selected) */
768  else settings.rightPage = 0;
769 
770  /* Indicate that a certain page needs to be updated after switching between them */
771  if (settings.rightPage == 0)
772  {
774  }
775  else if (settings.rightPage == 1)
776  {
778  }
779  else if (settings.rightPage == 2)
780  {
781  cData.newRunHours = 1;
782  cData.newRunMins = 1;
783  cData.newRunSecs = 1;
784  }
785  }
786  }
787  }
788  }
789  else if (steps < 0)
790  {
791  for (int8_t counter = 0; counter > steps; counter--)
792  {
793  /* Rotate LEFT display pages (if selected) */
794  if (settings.selectedDisplay == 0)
795  {
796  if (settings.leftPage > 0) settings.leftPage--;
797  else settings.leftPage = 2;
798 
799  /* Indicate that a certain page needs to be updated */
800  if (settings.leftPage == 0)
801  {
802  cData.newPow = 1;
803  cData.newMaxVolt = 1;
804  cData.newMinVolt = 1;
805  }
806  else if (settings.leftPage == 1)
807  {
808  cData.newPow = 1;
809  cData.newMaxCurr = 1;
810  cData.newMinCurr = 1;
811  }
812  else if (settings.leftPage == 2)
813  {
814  cData.newPow = 1;
815  cData.newAh = 1;
816  cData.newWh = 1;
817  }
818  }
819 
820  /* Rotate RIGHT display pages (if selected) */
821  if (settings.selectedDisplay == 1)
822  {
823  /* Change period (if selected) */
824  if (settings.changePeriod == 1)
825  {
826  if (settings.measPeriodIndex > 0)
827  {
832  }
833  }
834  /* Change day (if selected) */
835  else if (settings.changeDateTime == 1)
836  {
837  if ((rtc.latest == 0) && (rtc.day0 > 1)) rtc.day0--;
838  else if ((rtc.latest == 1) && (rtc.day1 > 1)) rtc.day1--;
839 
841  }
842  /* Change month (if selected) */
843  else if (settings.changeDateTime == 2)
844  {
845  if ((rtc.latest == 0) && (rtc.month0 > 1)) rtc.month0--;
846  else if ((rtc.latest == 1) && (rtc.month1 > 1)) rtc.month1--;
847 
849  }
850  /* Change year (if selected) */
851  else if (settings.changeDateTime == 3)
852  {
853  if (rtc.latest == 0) rtc.year0--;
854  else if (rtc.latest == 1) rtc.year1--;
855 
857  }
858  /* Change hour (if selected) */
859  else if (settings.changeDateTime == 4)
860  {
861  if ((rtc.latest == 0) && (rtc.hour0 > 0)) rtc.hour0--;
862  else if ((rtc.latest == 1) && (rtc.hour1 > 0)) rtc.hour1--;
863 
865  }
866  /* Change minutes (if selected) */
867  else if (settings.changeDateTime == 5)
868  {
869  if ((rtc.latest == 0) && (rtc.min0 > 0)) rtc.min0--;
870  else if ((rtc.latest == 1) && (rtc.min1 > 0)) rtc.min1--;
871 
873  }
874  /* Change seconds (if selected) */
875  else if (settings.changeDateTime == 6)
876  {
877  if ((rtc.latest == 0) && (rtc.sec0 > 0)) rtc.sec0--;
878  else if ((rtc.latest == 1) && (rtc.sec1 > 0)) rtc.sec1--;
879 
881  }
882  else
883  {
884  /* Rotate right display pages (if selected) */
886  else settings.rightPage = 2;
887 
888  /* Indicate that a certain page needs to be updated */
889  if (settings.rightPage == 0)
890  {
892  }
893  else if (settings.rightPage == 1)
894  {
896  }
897  else if (settings.rightPage == 2)
898  {
899  cData.newRunHours = 1;
900  cData.newRunMins = 1;
901  cData.newRunSecs = 1;
902  }
903  }
904  }
905  }
906  }
907 
908  /* Update field with new value */
909  settings.oldTIM3 = encValue;
910  }
911 }

◆ checkSwitches()

void checkSwitches ( void  )

Function to handle switch presses.

Definition at line 543 of file main.c.

544 {
545  /* UP */
546  if (getSW0())
547  {
548  if ((settings.mode == 0) && !(settings.headerSaved) && (settings.changeDateTime == 0))
549  {
550  settings.mode = 1;
551  settings.newMode = 1; /* Display needs updating */
552  }
553  else if (settings.mode == 1)
554  {
555  settings.mode = 0;
556  settings.newMode = 1; /* Display needs updating */
557  }
558 
559  setSW0(0); /* Interrupt handled */
560  }
561 
562  /* DOWN */
563  if (getSW1())
564  {
567 
568  settings.newSelDisplay = 1; /* Display needs updating */
569 
570  setSW1(0); /* Interrupt handled */
571  }
572 
573  /* Encoder press */
574  if (getENCkey())
575  {
576  /* Only change when not running */
577  if (settings.mode == 0)
578  {
579  /* Reset value on selected LEFT page */
580  if ((settings.selectedDisplay == 0) && (settings.leftPage == 0))
581  {
582  data.firstVolt = 1;
583  data.minVoltage = 0.0;
584  data.newMinVolt = 1;
585  data.maxVoltage = 0.0;
586  data.newMaxVolt = 1;
587  }
588  else if ((settings.selectedDisplay == 0) && (settings.leftPage == 1))
589  {
590  data.firstCurr = 1;
591  data.minCurrent = 0.0;
592  data.newMinCurr = 1;
593  data.maxCurrent = 0.0;
594  data.newMaxCurr = 1;
595  }
596  else if ((settings.selectedDisplay == 0) && (settings.leftPage == 2))
597  {
598  data.ah = 0.0;
599  data.newAh = 1;
600  data.wh = 0.0;
601  data.newWh = 1;
602  }
603 
604  /* Set values on selected RIGHT page */
605  if ((settings.selectedDisplay == 1) && (settings.rightPage == 0))
606  {
607  if (settings.changeDateTime == 0)
608  {
609  settings.changeDateTime = 1; /* Start by changing day */
610  settings.dateTimeChanged = 1; /* Force display update (a.o. underline) */
611  }
612  else if (settings.changeDateTime == 6) /* End by changing seconds */
613  {
615  settings.dateTimeChanged = 1; /* Force display update (a.o. underline) */
616  writeToRTC();
617  }
618  else if (settings.changeDateTime > 0)
619  {
622  }
623  }
624  else if ((settings.selectedDisplay == 1) && (settings.rightPage == 1))
625  {
626  /* Enable/disable period change on encoder turn */
627  if (settings.changePeriod == 0)
628  {
631  }
632  else if (settings.changePeriod == 1)
633  {
636  }
637  }
638  /* Directly change period on encoder press on runtime page */
639  else if ((settings.selectedDisplay == 1) && (settings.rightPage == 2))
640  {
641  settings.rightPage = 1;
644  }
645  }
646 
647  setENCkey(0); /* Interrupt handled */
648  }
649 }

◆ Error_Handler()

void Error_Handler ( void  )

This function is executed in case of error occurrence.

Return values
None

Definition at line 1102 of file main.c.

1103 {
1104  /* USER CODE BEGIN Error_Handler_Debug */
1105  /* User can add his own implementation to report the HAL error return state */
1106  __disable_irq();
1107  while (1)
1108  {
1109  }
1110  /* USER CODE END Error_Handler_Debug */
1111 }

◆ main()

int main ( void  )

The application entry point.

Return values
int

Definition at line 181 of file main.c.

182 {
183  /* USER CODE BEGIN 1 */
184 
185  /* USER CODE END 1 */
186 
187  /* MCU Configuration--------------------------------------------------------*/
188 
189  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
190  HAL_Init();
191 
192  /* USER CODE BEGIN Init */
193 
194  /* USER CODE END Init */
195 
196  /* Configure the system clock */
198 
199  /* USER CODE BEGIN SysInit */
200 
201  /* USER CODE END SysInit */
202 
203  /* Initialize all configured peripherals */
204  MX_GPIO_Init();
205 // MX_ADC1_Init();
206  MX_I2C1_Init();
207  MX_ADC2_Init();
208  MX_TIM3_Init();
209 // MX_TIM2_Init();
210  /* USER CODE BEGIN 2 */
211 
212  /* Power down UART voltage output */
213  LL_GPIO_SetOutputPin(UART1_3V3out_GPIO_Port, UART1_3V3out_Pin); /* P-MOS so inverted logic! */
214 
215  /* Power up UART DTR (~reset)-pin so we're not resetting the module */
216  LL_GPIO_SetOutputPin(UART1_DTRout_GPIO_Port, UART1_DTRout_Pin);
217 
218 
219  /* Enable output channel 1 (rotary encoder) */
220  LL_TIM_CC_EnableChannel(TIM3, LL_TIM_CHANNEL_CH1 | LL_TIM_CHANNEL_CH2);
221 
222  /* Enable counter */
223  LL_TIM_EnableCounter(TIM3);
224 
225  /* Force update generation */
226  LL_TIM_GenerateEvent_UPDATE(TIM3);
227 
228 
229  /* Initialize date/time functionality */
230  RTC_Init();
231 
232 
233  /* Enable the update interrupt (logging-timing) */
234 // LL_TIM_EnableIT_UPDATE(TIM2); // TODO change to buzzer timing
235 
236  /* Enable counter */
237 // LL_TIM_EnableCounter(TIM2); // TODO change to buzzer timing
238 
239  /* Force update generation */
240 // LL_TIM_GenerateEvent_UPDATE(TIM2); // TODO change to buzzer timing
241 
242 
243  /* Initialize UART functionality */
244  USART1_INIT();
245  USART2_INIT();
246  USART3_INIT();
247 
248  /* Initialize structs */
249  initDataStruct();
251 
252  /* Activate ADC for battery voltage measurements */
253  Activate_ADC();
254 
255  /* Clear Terminal for Segger RTT functionality */
256  SEGGER_RTT_WriteString(0, RTT_CTRL_CLEAR"START\r\n");
257 
258  /* Initialize displays */
259  initDisplays();
260 
261  /* Disable buzzer */
262 // LL_GPIO_ResetOutputPin(Buzzer_GPIO_Port, Buzzer_Pin);
263 
264 // LL_GPIO_TogglePin(Relay_GPIO_Port, Relay_Pin);
265 // LL_GPIO_TogglePin(Buzzer_GPIO_Port, Buzzer_Pin);
266 
267 
268 // SEGGER_RTT_printf(0, "%sTime:%s%s %.7d\n\r",
269 // RTT_CTRL_RESET,
270 // RTT_CTRL_BG_BRIGHT_RED,
271 // RTT_CTRL_TEXT_BRIGHT_WHITE,
272 // 1111111
273 // );
274 // SEGGER_RTT_WriteString(0, RTT_CTRL_RESET"Red: "RTT_CTRL_TEXT_BRIGHT_RED"This text is red."RTT_CTRL_TEXT_BLACK""RTT_CTRL_BG_BRIGHT_RED"This background is red. "RTT_CTRL_RESET"Normal text again.\n\r");
275 // SEGGER_RTT_printf(0, "Value = %x\r\n", value);
276 // SEGGER_RTT_printf(0, RTT_CTRL_RESET"%-.3d ", value);
277 // SEGGER_RTT_printf(0, RTT_CTRL_TEXT_BRIGHT_RED"0x%.2x ", value);
278 
279 
280  /* USER CODE END 2 */
281 
282  /* Infinite loop */
283  /* USER CODE BEGIN WHILE */
284  while (1)
285  {
286  /* USER CODE END WHILE */
287 
288  /* USER CODE BEGIN 3 */
289 
290  checkEncoder();
291  checkSwitches();
292 
293  USART2_SendCommand(aBY56W_SingleRead, 4); /* Voltage */
294  USART3_SendCommand(aBY56W_SingleRead, 4); /* Current */
295 
296  uint16_t whileLoops = 0;
297 
298  while (!(data.voltReceived) && !(data.currReceived) && (whileLoops < MAX_WHILELOOPS_METER_RESPONSE))
299  {
301  whileLoops++;
302  }
303 
304 // SEGGER_RTT_printf(0, "%d ", whileLoops);
305 
306  if (whileLoops < MAX_WHILELOOPS_METER_RESPONSE)
307  {
308  /* Store tick-value of latest measurements */
309  if (data.latestMScounter == 0)
310  {
312  data.latestMScounter = 1;
313  }
314  else if (data.latestMScounter == 1)
315  {
317  data.latestMScounter = 0;
318  }
319  }
320 
321  /* Calculate power, Ah and Wh */
323  {
325  if (data.power > 999.999) data.power = 999.999;
326 
327  /* Convert floats to uint32_t (power has 3 decimal places: *1000) */
328  uint32_t oldPower = FLOAT_TO_INT(data.oldPower*1000);
329  uint32_t power = FLOAT_TO_INT(data.power*1000);
330 
331  if (oldPower != power)
332  {
334  data.newPow = 1;
335  }
336 
337  /* Calculate time between measurements */
338  uint32_t msTime = 0;
339  if (data.latestMScounter == 0) msTime = data.msCounter0 - data.msCounter1;
340  else if (data.latestMScounter == 1) msTime = data.msCounter1 - data.msCounter0;
341 
342 // SEGGER_RTT_printf(0, "%dms ", msTime);
343 
344  data.ah += data.current * ((float)msTime / 3600000.0);
345  if (data.ah > 9999.999) data.ah = 9999.999;
346 
347  data.newAh = 1;
348 
349  data.wh += data.power * ((float)msTime / 3600000.0);
350  if (data.wh > 9999.999) data.wh = 9999.999;
351 
352  data.newWh = 1;
353  }
354 
355  data.voltReceived = 0;
356  data.currReceived = 0;
357 
358  /* Change variables if one second is elapsed */
359  if (decSecondPassed())
360  {
361  /* Signal (second) conversion logic */
362  settings.newSec = 1;
363 
364  /* Get new RTC values if not changing the date/time manually */
365  if ((settings.changeDateTime == 0) && (settings.secPassed == 0)) readFromRTC();
366 
367  /* Update runtime if running */
368  if ((settings.mode == 1) && settings.loggerReady)
369  {
371 
372  /* Re-calculate runtime */
373  if (data.runSecs < 59) data.runSecs++;
374  else if (data.runSecs == 59)
375  {
376  if (data.runMins < 59) data.runMins++;
377  else if (data.runMins == 59)
378  {
379  if (data.runHours < 99) data.runHours++;
380 
381  data.newRunHours = 1; /* Display needs updating */
382  data.runMins = 0;
383  }
384  data.newRunMins = 1; /* Display needs updating */
385  data.runSecs = 0;
386  }
387  data.newRunSecs = 1; /* Display needs updating */
388  }
389 
391  else if (settings.vbatCounter == 0)
392  {
395  }
396  }
397 
398  convertValues();
399 
400  /* Stopped, Logger needs to be disabled */
401  if ((settings.mode == 0) && settings.headerSaved)
402  {
403  if (settings.msWaiting == 0)
404  {
405  /* Make sure all values are converted to chars */
406  forceConversion();
407 
408  displaySaveIcon();
409  writeFooter();
410 
411  /* Set waiting time */
414  }
416  {
417  /* Disable power to logger */
418  LL_GPIO_SetOutputPin(UART1_3V3out_GPIO_Port, UART1_3V3out_Pin); /* P-MOS so inverted logic! */
419 
420  /* Reset waiting functionality */
421  settings.msWaiting = 0;
423 
424  settings.loggerReady = 0;
425  settings.headerSaved = 0;
426  }
427  }
428 
429  /* Just started, header not yet saved */
430  if ((settings.mode == 1) && !(settings.headerSaved))
431  {
432  if (settings.msWaiting == 0)
433  {
434  settings.remMeasPeriodS = 0; /* Force immediate measurement */
435  settings.rightPage = 2; /* Go to runtime sub-page */
436 
437  /* Enable power to logger */
438  LL_GPIO_ResetOutputPin(UART1_3V3out_GPIO_Port, UART1_3V3out_Pin); /* P-MOS so inverted logic! */
439 
440  /* Set waiting-time */
443  }
445  {
446  /* Reset values */
447  initDataStruct();
448  convertValues();
449 
450  displaySaveIcon();
451  writeHeader();
452 
453  /* Reset waiting functionality */
454  settings.msWaiting = 0;
456 
457  settings.loggerReady = 1; /* If we got here by timeout make sure the runtime logic will still work */
458  settings.headerSaved = 1;
459  }
460  }
461  /* Running, header already saved, amount of seconds necessary reached */
462  else if ((settings.mode == 1) && settings.headerSaved && (settings.remMeasPeriodS == 0))
463  {
465 
466  displaySaveIcon();
468 
469  }
470 
474 
475  /* Insert millisecond delay */
476 // SEGGER_RTT_printf(0, "Loop ");
477 // LL_mDelay(250);
478 
479  }
480  /* USER CODE END 3 */
481 }

◆ SystemClock_Config()

void SystemClock_Config ( void  )

System Clock Configuration.

Return values
None

Definition at line 487 of file main.c.

488 {
489  LL_FLASH_SetLatency(LL_FLASH_LATENCY_0);
490  while(LL_FLASH_GetLatency()!= LL_FLASH_LATENCY_0)
491  {
492  }
493  LL_RCC_HSI_SetCalibTrimming(16);
494  LL_RCC_HSI_Enable();
495 
496  /* Wait till HSI is ready */
497  while(LL_RCC_HSI_IsReady() != 1)
498  {
499 
500  }
501  LL_PWR_EnableBkUpAccess();
502  LL_RCC_LSE_Enable();
503 
504  /* Wait till LSE is ready */
505  while(LL_RCC_LSE_IsReady() != 1)
506  {
507 
508  }
509  if(LL_RCC_GetRTCClockSource() != LL_RCC_RTC_CLKSOURCE_LSE)
510  {
511  LL_RCC_ForceBackupDomainReset();
512  LL_RCC_ReleaseBackupDomainReset();
513  LL_RCC_SetRTCClockSource(LL_RCC_RTC_CLKSOURCE_LSE);
514  }
515  LL_RCC_EnableRTC();
516  LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1);
517  LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_1);
518  LL_RCC_SetAPB2Prescaler(LL_RCC_APB2_DIV_1);
519  LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_HSI);
520 
521  /* Wait till System clock is ready */
522  while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_HSI)
523  {
524 
525  }
526  LL_SetSystemCoreClock(8000000);
527 
528  /* Update the time base */
530  {
531  Error_Handler();
532  }
533  LL_RCC_SetADCClockSource(LL_RCC_ADC_CLKSRC_PCLK2_DIV_2);
534 }

◆ writeFooter()

void writeFooter ( void  )

Function to print the footer of the logfile (two lines).

This function prints data which doesn't change every measurement.
This function also prints debugging information using Segger RTT.

Definition at line 936 of file main.c.

937 {
938  /* Print footer-header */
939  USART1_print("Total runtime [hour:min:sec],Capacity [Ah],Capacity [Wh],Max Volt [V],Min Volt [V],Max Curr [A],Min Curr [A]\r\n");
940  SEGGER_RTT_printf(0, "Total runtime [hour:min:sec], Capacity [Ah], Capacity [Wh], Max Volt [V], Min Volt [V], Max Curr [A], Min Curr [A]\r\n");
941 
942  /* Print RUNTIME */
944  USART1_print(":");
946  USART1_print(":");
949 
950  /* Print spacing */
951  USART1_print(",");
952  SEGGER_RTT_printf(0, " ");
953 
954 
955  /* Print CAPACITY Ah */
957  USART1_print(".");
959  SEGGER_RTT_printf(0, "%s.%s Ah", cData.ahLeft, cData.ahRight);
960 
961  /* Print spacing */
962  USART1_print(",");
963  SEGGER_RTT_printf(0, " ");
964 
965 
966  /* Print CAPACITY Wh */
968  USART1_print(".");
970  SEGGER_RTT_printf(0, "%s.%s Wh", cData.whLeft, cData.whRight);
971 
972  /* Print spacing */
973  USART1_print(",");
974  SEGGER_RTT_printf(0, " ");
975 
976 
977  /* Print MAX VOLTAGE */
979  USART1_print(".");
982 
983  /* Print spacing */
984  USART1_print(",");
985  SEGGER_RTT_printf(0, " ");
986 
987 
988  /* Print MIN VOLTAGE */
990  USART1_print(".");
993 
994  /* Print spacing */
995  USART1_print(",");
996  SEGGER_RTT_printf(0, " ");
997 
998 
999  /* Print MAX CURRENT */
1001  USART1_print(".");
1004 
1005  /* Print spacing */
1006  USART1_print(",");
1007  SEGGER_RTT_printf(0, " ");
1008 
1009 
1010  /* Print MIN CURRENT */
1012  USART1_print(".");
1015 
1016  /* Print spacing */
1017  USART1_print("\r\n");
1018  SEGGER_RTT_printf(0, "\r\n");
1019 }

◆ writeHeader()

void writeHeader ( void  )

Function to print the header of the logfile to indicate the columns.

This function also prints debugging information using Segger RTT.

Definition at line 921 of file main.c.

922 {
923  USART1_print("Date and Time [day/month/year hour:min:sec],Runtime [hour:min:sec],Voltage [V],Current [A],Power [W]\r\n");
924  SEGGER_RTT_printf(0, "Date and Time [day/month/year hour:min:sec], Runtime [hour:min:sec], Voltage [V], Current [A], Power [W]\r\n");
925 }

◆ writeMeasurements()

void writeMeasurements ( void  )

Function to print measurement data.

This function also prints debugging information using Segger RTT.

Definition at line 1029 of file main.c.

1030 {
1031  /* Print DATE and TIME */
1033  USART1_print("/");
1035  USART1_print("/");
1037  USART1_print(" ");
1039  USART1_print(":");
1041  USART1_print(":");
1044 
1045  /* Print spacing */
1046  USART1_print(",");
1047  SEGGER_RTT_printf(0, " ");
1048 
1049 
1050  /* Print RUNTIME */
1052  USART1_print(":");
1054  USART1_print(":");
1057 
1058  /* Print spacing */
1059  USART1_print(",");
1060  SEGGER_RTT_printf(0, " ");
1061 
1062 
1063  /* Print VOLTAGE */
1065  USART1_print(".");
1067  SEGGER_RTT_printf(0, "%s.%s V", cData.vLeft, cData.vRight);
1068 
1069  /* Print spacing */
1070  USART1_print(",");
1071  SEGGER_RTT_printf(0, " ");
1072 
1073 
1074  /* Print CURRENT */
1076  USART1_print(".");
1078  SEGGER_RTT_printf(0, "%s.%s A", cData.aLeft, cData.aRight);
1079 
1080  /* Print spacing */
1081  USART1_print(",");
1082  SEGGER_RTT_printf(0, " ");
1083 
1084 
1085  /* Print POWER */
1087  USART1_print(".");
1089  SEGGER_RTT_printf(0, "%s.%s W", cData.pLeft, cData.pRight);
1090 
1091  /* Print spacing */
1092  USART1_print("\r\n");
1093  SEGGER_RTT_printf(0, "\r\n");
1094 }

Variable Documentation

◆ aBY56W_SingleRead

const uint8_t aBY56W_SingleRead[4] = {0x88, 0xAE, 0x00, 0x11}
static

Definition at line 142 of file main.c.

◆ cData

struct cData_t cData

Definition at line 148 of file main.c.

◆ data

struct Data_t data

Definition at line 147 of file main.c.

◆ LUT_period_s

const uint16_t LUT_period_s[16] = {0, 1, 2, 5, 10, 15, 30, 60, 120, 300, 600, 900, 1800, 3600, 7200, 18000}
static

LUT for period selection [s] (0 = as fast as possible)

Definition at line 153 of file main.c.

◆ LUT_period_s_length

const uint8_t LUT_period_s_length = 16
static

Length of LUT

Definition at line 156 of file main.c.

◆ rtc

struct RTC_dateTime_t rtc

Definition at line 150 of file main.c.

◆ settings

struct Settings_t settings

Definition at line 149 of file main.c.

Settings_t::headerSaved
uint8_t headerSaved
Definition: conversion.h:161
Data_t::runSecs
uint8_t runSecs
Definition: conversion.h:85
RTC_dateTime_t::hour0
uint8_t hour0
Definition: rtc.h:55
Settings_t::rightPage
uint8_t rightPage
Definition: conversion.h:151
Settings_t::selectedDisplay
uint8_t selectedDisplay
Definition: conversion.h:148
displaySaveIcon
void displaySaveIcon(void)
Function to display the SAVE status icon on the RIGHT OLED screen.
Definition: display.c:558
checkSwitches
void checkSwitches(void)
Function to handle switch presses.
Definition: main.c:543
startBattVoltMeasurement
void startBattVoltMeasurement(void)
Definition: adc.c:264
Data_t::current
float current
Definition: conversion.h:64
checkEncoder
void checkEncoder(void)
Function to handle encoder rotation logic.
Definition: main.c:656
data
struct Data_t data
Definition: main.c:147
USART1_INIT
void USART1_INIT(void)
USART1 Initialization Function.
Definition: usart.c:365
cData_t::aMaxLeft
char aMaxLeft[2]
Definition: conversion.h:109
HAL_InitTick
HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
cData_t::aMaxRight
char aMaxRight[5]
Definition: conversion.h:110
__disable_irq
#define __disable_irq
Definition: cmsis_iccarm.h:545
RTC_dateTime_t::min1
uint8_t min1
Definition: rtc.h:61
LL_SetSystemCoreClock
void LL_SetSystemCoreClock(uint32_t HCLKFrequency)
This function sets directly SystemCoreClock CMSIS variable.
Definition: stm32f1xx_ll_utils.c:240
cData_t::newMaxCurr
uint8_t newMaxCurr
Definition: conversion.h:111
cData_t::newRunHours
uint8_t newRunHours
Definition: conversion.h:128
Settings_t::msWaiting
uint16_t msWaiting
Definition: conversion.h:168
Data_t::newRunHours
uint8_t newRunHours
Definition: conversion.h:82
cData_t::ahLeft
char ahLeft[5]
Definition: conversion.h:120
Settings_t::leftPage
uint8_t leftPage
Definition: conversion.h:150
Data_t::newMaxVolt
uint8_t newMaxVolt
Definition: conversion.h:61
RTT_CTRL_CLEAR
#define RTT_CTRL_CLEAR
Definition: SEGGER_RTT.h:373
cData_t::newPow
uint8_t newPow
Definition: conversion.h:118
SHUTDOWN_DELAY_UART_MODULE_MS
#define SHUTDOWN_DELAY_UART_MODULE_MS
Definition: main.c:122
getSW1
uint8_t getSW1(void)
Definition: gpio.c:51
Data_t::maxCurrent
float maxCurrent
Definition: conversion.h:70
Data_t::maxVoltage
float maxVoltage
Definition: conversion.h:60
Data_t::newAh
uint8_t newAh
Definition: conversion.h:77
USART2_SendCommand
void USART2_SendCommand(uint8_t *command, uint8_t bytes)
Send a byte (uint8_t) command to USART2.
Definition: usart.c:299
cData_t::charRunHours
char charRunHours[3]
Definition: conversion.h:127
Data_t::runHours
uint8_t runHours
Definition: conversion.h:81
cData_t::aRight
char aRight[5]
Definition: conversion.h:107
USART3_SendCommand
void USART3_SendCommand(uint8_t *command, uint8_t bytes)
Send a byte (uint8_t) command to USART3.
Definition: usart.c:334
UART1_DTRout_GPIO_Port
#define UART1_DTRout_GPIO_Port
Definition: main.h:134
Data_t::newRunSecs
uint8_t newRunSecs
Definition: conversion.h:86
RTC_dateTime_t::charMonth
char charMonth[3]
Definition: rtc.h:79
RTC_dateTime_t::charHour
char charHour[3]
Definition: rtc.h:57
Settings_t::loggerReady
volatile uint8_t loggerReady
Definition: conversion.h:167
settings
struct Settings_t settings
Definition: main.c:149
RTC_dateTime_t::year0
uint16_t year0
Definition: rtc.h:82
Data_t::wh
float wh
Definition: conversion.h:78
Settings_t::changePeriod
uint8_t changePeriod
Definition: conversion.h:156
Data_t::newMinVolt
uint8_t newMinVolt
Definition: conversion.h:59
getSW0
uint8_t getSW0(void)
Definition: gpio.c:41
Data_t::currReceived
uint8_t currReceived
Definition: conversion.h:63
cData_t::vLeft
char vLeft[3]
Definition: conversion.h:96
Data_t::msCounter0
uint32_t msCounter0
Definition: conversion.h:88
aBY56W_SingleRead
static const uint8_t aBY56W_SingleRead[4]
Definition: main.c:142
Settings_t::newMode
uint8_t newMode
Definition: conversion.h:147
cData_t::whLeft
char whLeft[5]
Definition: conversion.h:123
cData
struct cData_t cData
Definition: main.c:148
cData_t::whRight
char whRight[4]
Definition: conversion.h:124
UART1_DTRout_Pin
#define UART1_DTRout_Pin
Definition: main.h:133
RTC_dateTime_t::charSec
char charSec[3]
Definition: rtc.h:67
RTC_dateTime_t::charDay
char charDay[3]
Definition: rtc.h:72
writeMeasurements
void writeMeasurements(void)
Function to print measurement data.
Definition: main.c:1029
cData_t::pLeft
char pLeft[4]
Definition: conversion.h:116
cData_t::charRunMins
char charRunMins[3]
Definition: conversion.h:129
cData_t::pRight
char pRight[4]
Definition: conversion.h:117
setSW1
void setSW1(uint8_t value)
Definition: gpio.c:56
cData_t::newMaxVolt
uint8_t newMaxVolt
Definition: conversion.h:101
setSW0
void setSW0(uint8_t value)
Definition: gpio.c:46
MX_ADC2_Init
void MX_ADC2_Init(void)
Definition: adc.c:140
Settings_t::measPeriodIndex
uint8_t measPeriodIndex
Definition: conversion.h:153
TIM3
#define TIM3
Definition: stm32f103xb.h:649
cData_t::newAh
uint8_t newAh
Definition: conversion.h:122
RTC_dateTime_t::min0
uint8_t min0
Definition: rtc.h:60
STARTUP_DELAY_UART_MODULE_MS
#define STARTUP_DELAY_UART_MODULE_MS
Definition: main.c:125
USART_HandleContinuousReception
void USART_HandleContinuousReception(void)
This function monitors USART1/2/3 buffer filling indication and moves data around accordingly.
Definition: usart.c:114
RTC_dateTime_t::month0
uint8_t month0
Definition: rtc.h:77
cData_t::aLeft
char aLeft[2]
Definition: conversion.h:106
Data_t::voltage
float voltage
Definition: conversion.h:54
convertValues
void convertValues(void)
Function to convert the measurements to char-arrays.
Definition: conversion.c:172
Data_t::oldPower
float oldPower
Definition: conversion.h:74
RTC_dateTime_t::year1
uint16_t year1
Definition: rtc.h:83
Settings_t::periodChanged
uint8_t periodChanged
Definition: conversion.h:157
cData_t::vRight
char vRight[4]
Definition: conversion.h:97
LUT_period_s_length
static const uint8_t LUT_period_s_length
Definition: main.c:156
TICK_INT_PRIORITY
#define TICK_INT_PRIORITY
Definition: stm32f1xx_hal_conf.h:131
Data_t::power
float power
Definition: conversion.h:73
cData_t::aMinRight
char aMinRight[5]
Definition: conversion.h:113
Settings_t::dateTimeChanged
uint8_t dateTimeChanged
Definition: conversion.h:159
RTC_dateTime_t::charMin
char charMin[3]
Definition: rtc.h:62
Data_t::newMinCurr
uint8_t newMinCurr
Definition: conversion.h:69
Settings_t::measPeriodS
uint16_t measPeriodS
Definition: conversion.h:155
UART1_3V3out_GPIO_Port
#define UART1_3V3out_GPIO_Port
Definition: main.h:125
cData_t::charRunSecs
char charRunSecs[3]
Definition: conversion.h:131
Settings_t::vbatCounter
uint8_t vbatCounter
Definition: conversion.h:144
writeHeader
void writeHeader(void)
Function to print the header of the logfile to indicate the columns.
Definition: main.c:921
initDataStruct
void initDataStruct(void)
Function to initialize/reset the values in the data-struct.
Definition: conversion.c:69
cData_t::newMinCurr
uint8_t newMinCurr
Definition: conversion.h:114
RTC_dateTime_t::charYear
char charYear[5]
Definition: rtc.h:84
RTC_dateTime_t::sec1
uint8_t sec1
Definition: rtc.h:66
writeToRTC
void writeToRTC(void)
Function to update the RTC values according to the data in the struct.
Definition: rtc.c:319
updateRightDisplay
void updateRightDisplay(void)
Function to display updated info on the RIGHT OLED screen.
Definition: display.c:276
cData_t::ahRight
char ahRight[4]
Definition: conversion.h:121
Data_t::msCounter1
uint32_t msCounter1
Definition: conversion.h:89
initDisplays
void initDisplays(void)
Function to initialize the OLED displays.
Definition: display.c:71
forceConversion
void forceConversion(void)
Function to force a conversion of all values not (yet) displayed on the OLED screens.
Definition: conversion.c:155
Data_t::firstVolt
uint8_t firstVolt
Definition: conversion.h:56
Settings_t::newSelDisplay
uint8_t newSelDisplay
Definition: conversion.h:149
VBAT_MEASUREMENT_PERIOD_S
#define VBAT_MEASUREMENT_PERIOD_S
Definition: main.c:128
SEGGER_RTT_WriteString
unsigned SEGGER_RTT_WriteString(unsigned BufferIndex, const char *s)
Definition: SEGGER_RTT.c:1215
LUT_period_s
static const uint16_t LUT_period_s[16]
Definition: main.c:153
HAL_OK
@ HAL_OK
Definition: stm32f1xx_hal_def.h:41
cData_t::newMinVolt
uint8_t newMinVolt
Definition: conversion.h:104
RTC_dateTime_t::day0
uint8_t day0
Definition: rtc.h:70
Settings_t::changeDateTime
uint8_t changeDateTime
Definition: conversion.h:158
Data_t::minVoltage
float minVoltage
Definition: conversion.h:58
Data_t::newMaxCurr
uint8_t newMaxCurr
Definition: conversion.h:71
cData_t::vMaxRight
char vMaxRight[4]
Definition: conversion.h:100
getENCkey
uint8_t getENCkey(void)
Definition: gpio.c:61
SystemClock_Config
void SystemClock_Config(void)
System Clock Configuration.
Definition: main.c:487
Data_t::ah
float ah
Definition: conversion.h:76
HAL_Init
HAL_StatusTypeDef HAL_Init(void)
cData_t::aMinLeft
char aMinLeft[2]
Definition: conversion.h:112
Settings_t::msWaitingStart
uint32_t msWaitingStart
Definition: conversion.h:169
RTC_dateTime_t::sec0
uint8_t sec0
Definition: rtc.h:65
Data_t::voltReceived
uint8_t voltReceived
Definition: conversion.h:53
Data_t::newWh
uint8_t newWh
Definition: conversion.h:79
RTC_dateTime_t::latest
uint8_t latest
Definition: rtc.h:53
cData_t::vMaxLeft
char vMaxLeft[3]
Definition: conversion.h:99
Data_t::firstCurr
uint8_t firstCurr
Definition: conversion.h:66
rtc
struct RTC_dateTime_t rtc
Definition: main.c:150
UART1_3V3out_Pin
#define UART1_3V3out_Pin
Definition: main.h:124
decSecondPassed
uint8_t decSecondPassed(void)
Function to decrease the value of secondPassed.
Definition: conversion.c:447
Data_t::minCurrent
float minCurrent
Definition: conversion.h:68
cData_t::vMinRight
char vMinRight[4]
Definition: conversion.h:103
Settings_t::remMeasPeriodS
uint16_t remMeasPeriodS
Definition: conversion.h:154
Settings_t::oldTIM3
uint16_t oldTIM3
Definition: conversion.h:163
MX_TIM3_Init
void MX_TIM3_Init(void)
Definition: tim.c:51
cData_t::newRunMins
uint8_t newRunMins
Definition: conversion.h:130
RTC_dateTime_t::day1
uint8_t day1
Definition: rtc.h:71
cData_t::newRunSecs
uint8_t newRunSecs
Definition: conversion.h:132
readFromRTC
void readFromRTC(void)
Function to update the RTC data in the struct.
Definition: rtc.c:309
cData_t::newWh
uint8_t newWh
Definition: conversion.h:125
initSettingsStruct
void initSettingsStruct(void)
Function to initialize/reset the values in the settings-struct.
Definition: conversion.c:119
Settings_t::secPassed
volatile uint8_t secPassed
Definition: conversion.h:164
SEGGER_RTT_printf
int SEGGER_RTT_printf(unsigned BufferIndex, const char *sFormat,...)
Definition: SEGGER_RTT_printf.c:491
FLOAT_TO_INT
#define FLOAT_TO_INT(x)
Definition: conversion.h:48
HAL_GetTick
uint32_t HAL_GetTick(void)
MAX_WHILELOOPS_METER_RESPONSE
#define MAX_WHILELOOPS_METER_RESPONSE
Definition: main.c:119
USART3_INIT
void USART3_INIT(void)
USART3 Initialization Function.
Definition: usart.c:491
Data_t::runMins
uint8_t runMins
Definition: conversion.h:83
Settings_t::newSec
uint8_t newSec
Definition: conversion.h:165
USART1_print
void USART1_print(char *message)
Print a string (char array) to USARTx.
Definition: usart.c:556
USART2_INIT
void USART2_INIT(void)
USART2 Initialization Function.
Definition: usart.c:428
setENCkey
void setENCkey(uint8_t value)
Definition: gpio.c:66
Activate_ADC
void Activate_ADC(void)
Perform ADC activation procedure to make it ready to convert (ADC instance: ADC2).
Definition: adc.c:197
MX_I2C1_Init
void MX_I2C1_Init(void)
Definition: i2c.c:30
Data_t::latestMScounter
uint8_t latestMScounter
Definition: conversion.h:90
cData_t::vMinLeft
char vMinLeft[3]
Definition: conversion.h:102
Settings_t::mode
uint8_t mode
Definition: conversion.h:146
Data_t::newPow
uint8_t newPow
Definition: conversion.h:75
RTC_dateTime_t::hour1
uint8_t hour1
Definition: rtc.h:56
RTC_dateTime_t::month1
uint8_t month1
Definition: rtc.h:78
finishDisplayUpdates
void finishDisplayUpdates(void)
Function to finish the screen-updates.
Definition: display.c:548
Error_Handler
void Error_Handler(void)
This function is executed in case of error occurrence.
Definition: main.c:1102
updateLeftDisplay
void updateLeftDisplay(void)
Function to display updated info on the LEFT OLED screen.
Definition: display.c:94
MX_GPIO_Init
void MX_GPIO_Init(void)
Definition: gpio.c:81
Data_t::newRunMins
uint8_t newRunMins
Definition: conversion.h:84
writeFooter
void writeFooter(void)
Function to print the footer of the logfile (two lines).
Definition: main.c:936
RTC_Init
void RTC_Init(void)
Function to initialize the Real Time Clock.
Definition: rtc.c:97