IoT-MBBR (masterthesis)
Masterthesis upgrading a Moving Bed BioReactor with extra sensors and cloud connectivity.
main.c
Go to the documentation of this file.
1 /***************************************************************************//**
2  * @file main.c
3  * @brief The main file for the IoT-MBBR project.
4  * @version 1.1
5  * @author Brecht Van Eeckhoudt
6  *
7  * ******************************************************************************
8  *
9  * @section Versions
10  * *
11  * @li v1.0: Initial version.
12  * @li v1.1: Changed `float` to `float32_t`, added ADC measurement functionality.
13  *
14  * ******************************************************************************
15  *
16  * @section License
17  *
18  * **Copyright (C) 2020 - Brecht Van Eeckhoudt**
19  *
20  * This program is free software: you can redistribute it and/or modify
21  * it under the terms of the **GNU General Public License** as published by
22  * the Free Software Foundation, either **version 3** of the License, or
23  * (at your option) any later version.
24  *
25  * This program is distributed in the hope that it will be useful,
26  * but WITHOUT ANY WARRANTY; without even the implied warranty of
27  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28  * GNU General Public License for more details.
29  *
30  * *A copy of the GNU General Public License can be found in the `LICENSE`
31  * file along with this source code.*
32  *
33  * @n
34  *
35  * Some methods also use code obtained from examples from [Silicon Labs' GitHub](https://github.com/SiliconLabs/peripheral_examples).
36  * These sections are licensed under the Silabs License Agreement. See the file
37  * "Silabs_License_Agreement.txt" for details. Before using this software for
38  * any purpose, you must agree to the terms of that agreement.
39  *
40  ******************************************************************************/
41 
42 
43 #include <stdint.h> /* (u)intXX_t */
44 #include <stdbool.h> /* "bool", "true", "false" */
45 #include "em_device.h" /* Include necessary MCU-specific header file */
46 #include "em_chip.h" /* Chip Initialization */
47 #include "em_cmu.h" /* Clock management unit */
48 #include "em_gpio.h" /* General Purpose IO */
49 #include "em_rtc.h" /* Real Time Counter (RTC) */
50 
51 #include "pin_mapping.h" /* PORT and PIN definitions */
52 #include "debug_dbprint.h" /* Enable or disable printing to UART for debugging */
53 #include "delay.h" /* Delay functionality */
54 #include "util.h" /* Utility functionality */
55 #include "util_string.h" /* String functionality */
56 #include "interrupt.h" /* GPIO wake-up initialization and interrupt handlers */
57 #include "DS18B20.h" /* Functions related to the temperature sensor */
58 #include "adc.h" /* Internal voltage and temperature reading functionality */
59 #include "datatypes.h" /* Definitions of the custom data-types */
60 #include "serlcd.h" /* serLCD functionality */
61 #include "BG96_at_driver.h" /* BG96 functionality */
62 #include "VL53L1X.h" /* VL53L1X functionality */
63 #include "SHT30.h" /* SHT30 functionality */
64 
65 
66 /* Local definitions */
67 /** Enable (1) or disable (0) the `status = 0` message */
68 #define SEND_STATUS_BOOT 1
69 
70 /** Enable (1) or disable (0) sending of messages (for debugging) */
71 #define SENDING_DATA 1
72 
73 /** Maximum measured height for percent calculation [mm] */
74 #define MAX_LENGTH_MM 300
75 
76 /** Time between each wake-up in seconds
77  * @li max 500 seconds when using LFXO delay
78  * @li 3600 seconds (one hour) works fine when using ULFRCO delay */
79 #define WAKE_UP_PERIOD_S 3600 /* 360 = every 5 minutes */
80 
81 /* Local variables */
82 /** Keep the state of the state machine */
83 static MCU_State_t MCUstate; /* Static so it's always kept in memory (@data segment, space provided during compile time) */
84 
85 /** Keep the measurement data */
87 
88 
89 /**************************************************************************//**
90  * @brief
91  * Method to check if any interrupts are triggered by buttons.
92  *
93  * @return
94  * @li `true` - A button interrupt was triggered.
95  * @li `false` - No button interrupt was triggered.
96  *****************************************************************************/
97 bool checkBTNinterrupts (void)
98 {
99  bool value = false;
100 
101  if (BTN_getTriggered())
102  {
103  value = true;
104 
105  BTN_setTriggered(false); /* Clear variable */
106  }
107 
108  return (value);
109 }
110 
111 
112 /**************************************************************************//**
113  * @brief
114  * Method to display the latest measurements on the LCD.
115  *****************************************************************************/
116 void displayMeas (void)
117 {
118  setCursorLCD(1, 2); /* Move to second row, first column */
119  writeStringLCD("T1 ");
120 
121  uint16_t temp1 = data.t1;
122  if (data.t1 > 9999) temp1 /= 10; /* Remove last digit */ // TODO: round up/down!
123  uint8_t temp1L = temp1 / 100;
124  uint8_t temp1R = temp1 - ((temp1 / 100) * 100);
125  char temp1L_char[10]; // TODO: Use less chars here because we use uint16_t (check if values aren't bigger than 0xFFFF)
126  char temp1R_char[10]; // TODO: Use less chars here because we use uint16_t (check if values aren't bigger than 0xFFFF)
127  uint32_to_charDec(temp1L, temp1L_char); // TODO: display negative values!
128  uint32_to_charDec(temp1R, temp1R_char); // TODO: display negative values!
129 
130  writeStringLCD(temp1L_char);
131  writeStringLCD(".");
132  writeStringLCD(temp1R_char);
133  writeStringLCD("C T2 ");
134 
135  uint16_t temp2= data.t2;
136  if (data.t2 > 9999) temp2 /= 10; /* Remove last digit */ // TODO: round up/down!
137  uint8_t temp2L = temp2 / 100;
138  uint8_t temp2R = temp2 - ((temp2 / 100) * 100);
139  char temp2L_char[10]; // TODO: Use less chars here because we use uint16_t (check if values aren't bigger than 0xFFFF)
140  char temp2R_char[10]; // TODO: Use less chars here because we use uint16_t (check if values aren't bigger than 0xFFFF)
141  uint32_to_charDec(temp2L, temp2L_char); // TODO: display negative values!
142  uint32_to_charDec(temp2R, temp2R_char); // TODO: display negative values!
143 
144  writeStringLCD(temp2L_char);
145  writeStringLCD(".");
146  writeStringLCD(temp2R_char);
147  writeStringLCD("C");
148 
149  setCursorLCD(1, 3); /* Move to third row, first column */
150  writeStringLCD("N1 ");
151 
152  char nitr1_char[10]; // TODO: Use less chars here because we use uint16_t (check if values aren't bigger than 0xFFFF)
153  uint32_to_charDec(data.n1, nitr1_char);
154 
155  writeStringLCD(nitr1_char);
156  writeStringLCD("mA N2 ");
157 
158  char nitr2_char[10]; // TODO: Use less chars here because we use uint16_t (check if values aren't bigger than 0xFFFF)
159  uint32_to_charDec(data.n2, nitr2_char);
160 
161  writeStringLCD(nitr2_char);
162  writeStringLCD("mA C");
163 
164  char carbon_char[10]; // TODO: Use less chars here because we use uint16_t (check if values aren't bigger than 0xFFFF)
165  uint32_to_charDec(data.c, carbon_char);
166 
167  writeStringLCD(carbon_char);
168  writeStringLCD("%");
169 
170  setCursorLCD(1, 4); /* Move to fourth row, first column */
171  writeStringLCD("pH ");
172 
173  char pH_char[10]; // TODO: Use less chars here because we use uint16_t (check if values aren't bigger than 0xFFFF)
174  uint32_to_charDec(data.p, pH_char);
175 
176  writeStringLCD(pH_char);
177  writeStringLCD("mA RP ");
178 
179  char red_char[10]; // TODO: Use less chars here because we use uint16_t (check if values aren't bigger than 0xFFFF)
180  uint32_to_charDec(data.r, red_char);
181 
182  writeStringLCD(red_char);
183  writeStringLCD("mA");
184 
185  delay(50); /* Delay so all of the data is shown on the screen */
186 }
187 
188 
189 /**************************************************************************//**
190  * @brief
191  * Main function.
192  *****************************************************************************/
193 int main (void)
194 {
195  /* Value to keep the remaining sleep time when waking up using the accelerometer */
196  uint32_t remainingSleeptime = 0;
197 
198  while (1)
199  {
200  switch (MCUstate)
201  {
202  case INIT:
203  {
204  /* Initialize chip */
205  CHIP_Init();
206 
207 #if DEBUG_DBPRINT == 1 /* DEBUG_DBPRINT */
208  dbprint_INIT(DBG_UART, DBG_UART_LOC, false, false);
209 #endif /* DEBUG_DBPRINT */
210 
211  /* Initialize delay and sleep functionality */
212  delay(0);
213  sleep(2); /* Time so the I2C devices have time to start up */
214 
215  /* Initialize the LCD */
216  clearLCD();
217  writeStringLCD("Initializing...");
218 
219  /* Soft reset SHT30 */ // TODO: SHT30 (0x88) seems to respond to VL53L1X (0x52) commands and break the I2C bus until power cycle
220 // softResetSHT30();
221 // delay(2000);
222 
223  /* Initialize GPIO wake-up */
224  initGPIOwakeup();
225 
226  /* Init VL53L1X */
227  initVL53L1X();
228 
229  /* Init ADC */
230  initADC(CH1);
231 
232  /* Initialize BG96 */
233 #if SENDING_DATA == 1 /* SENDING_DATA */
234  initBG96();
235 #endif /* SENDING_DATA */
236 
237 #if SEND_STATUS_BOOT == 1 /* SEND_STATUS_BOOT */
238 
239 #if DEBUG_DBPRINT == 1 /* DEBUG_DBPRINT */
240  dbwarn("Sending startup status ...");
241 #endif /* DEBUG_DBPRINT */
242 
243  clearLCD();
244  writeStringLCD("Sending status (0)");
245 
246 #if SENDING_DATA == 1 /* SENDING_DATA */
247  sendStatus(0); /* Send the status data */
248 #endif /* SENDING_DATA */
249 
250 #endif /* SEND_STATUS_BOOT */
251 
252  MCUstate = MEASURE;
253  } break;
254 
255  case MEASURE:
256  {
257  clearLCD();
258  writeStringLCD("Measuring...");
259  displayMeas(); /* Display measurements on following rows */
260 
261  /* Measure and store the temperatures */
262  int32_t temp = readTempDS18B20(0);
263  if (temp < 0) data.t1 = 0;
264  else data.t1 = temp;
265 
266  temp = readTempDS18B20(1);
267  if (temp < 0) data.t2 = 0;
268  else data.t2 = temp;
269 
270 // data.t2 = SHT30_readTemp(); // TODO: SHT30 (0x88) seems to respond to VL53L1X (0x52) commands and break the I2C bus until power cycle
271 
272  /* Measure and store distance */
273  uint16_t distance = measureVL53L1X();
274 
275 #if DEBUG_DBPRINT == 1 /* DEBUG_DBPRINT */
276  dbinfoInt("distance = ", distance, " mm");
277 #endif /* DEBUG_DBPRINT */
278 
279  data.c = (uint16_t)(((float32_t)(MAX_LENGTH_MM - measureVL53L1X()) / MAX_LENGTH_MM) * 100.0);
280 
281  data.n1 = readADC(CH1);
282 
283  // TODO: measure more things!
284  data.n2 = 20;
285  data.p = 20;
286  data.r = 20;
287 
288 #if DEBUG_DBPRINT == 1 /* DEBUG_DBPRINT */
289  dbprintln("");
290  dbinfoInt(">> t1: ", data.t1, "");
291  dbinfoInt(">> t2: ", data.t2, "");
292  dbinfoInt(">> c: ", data.c, " %");
293  dbinfoInt(">> n1: ", data.n1, " mA");
294  dbinfoInt(">> n2: ", data.n2, " mA");
295  dbinfoInt(">> p: ", data.p, " mA");
296  dbinfoInt(">> r: ", data.r, " mA");
297  dbprintln("");
298 #endif /* DEBUG_DBPRINT */
299 
301  } break;
302 
303  case SEND_DATA:
304  {
305 
306 #if DEBUG_DBPRINT == 1 /* DEBUG_DBPRINT */
307  dbwarn("Sending measurements ...");
308 #endif /* DEBUG_DBPRINT */
309 
310  clearLCD();
311  writeStringLCD("Sending measurements");
312  displayMeas(); /* Display measurements on following rows */
313 
314 #if SENDING_DATA == 1 /* SENDING_DATA */
315  sendMeasurements(&data);
316 #endif /* SENDING_DATA */
317 
318  MCUstate = SLEEP;
319  } break;
320 
321  case SEND_STATUS:
322  {
323 
324 #if DEBUG_DBPRINT == 1 /* DEBUG_DBPRINT */
325  dbwarn("Sending STATUS ...");
326 #endif /* DEBUG_DBPRINT */
327 
328  // TODO
329 
330  MCUstate = SLEEP;
331  } break;
332 
333  case SLEEP:
334  {
335  clearLCD();
336  writeStringLCD("Waiting...");
337  displayMeas(); /* Display measurements on following rows */
338 
339 
340  sleep(WAKE_UP_PERIOD_S); /* Go to sleep for xx seconds */
341 
342  MCUstate = WAKEUP;
343  } break;
344 
345  case WAKEUP:
346  {
347  /* Check if we woke up using a button */
348  if (checkBTNinterrupts())
349  {
350 
351 #if DEBUG_DBPRINT == 1 /* DEBUG_DBPRINT */
352  dbprintln(" ");
353  dbprintln_color("BTN INT", 4);
354 #endif /* DEBUG_DBPRINT */
355 
356  remainingSleeptime = 0; /* Reset passed sleeping time */
357  }
358 
359  /* Check if we woke up using the RTC sleep functionality and act accordingly */
360  if (RTC_checkWakeup())
361  {
362  RTC_clearWakeup(); /* Clear variable */
363 
364 #if DEBUG_DBPRINT == 1 /* DEBUG_DBPRINT */
365  dbprintln(" ");
366  dbprintln_color("RTC INT", 4);
367 #endif /* DEBUG_DBPRINT */
368 
369  remainingSleeptime = 0; /* Reset passed sleeping time since it's an RTC wakeup */
370  }
371 
372  /* Check if we woke up because of a current pulse */
374  {
375  RTC_Enable(false); /* Disable the counter */
376 
377 #if DEBUG_DBPRINT == 1 /* DEBUG_DBPRINT */
378  dbprintln(" ");
379  dbprintln_color("PULSE INT", 4);
380 #endif /* DEBUG_DBPRINT */
381 
382  clearLCD();
383  writeStringLCD("Sending pulses...");
384  displayMeas(); /* Display measurements on following rows */
385 
386  if (PULSE_getTriggered(0))
387  {
388 
389 #if DEBUG_DBPRINT == 1 /* DEBUG_DBPRINT */
390  dbwarn("Sending P1 pulses ...");
391 #endif /* DEBUG_DBPRINT */
392 
393 #if SENDING_DATA == 1 /* SENDING_DATA */
394  sendPulse (0, 1);
395 #endif /* SENDING_DATA */
396 
397  PULSE_setTriggered(0, false);
398  }
399  else if (PULSE_getTriggered(1))
400  {
401 
402 #if DEBUG_DBPRINT == 1 /* DEBUG_DBPRINT */
403  dbwarn("Sending P2 pulses ...");
404 #endif /* DEBUG_DBPRINT */
405 
406 #if SENDING_DATA == 1 /* SENDING_DATA */
407  sendPulse (1, 1);
408 #endif /* SENDING_DATA */
409 
410  PULSE_setTriggered(1, false);
411  }
412 
413  MCUstate = WAKEUP;
414 
415  clearLCD();
416  writeStringLCD("Waiting...");
417  displayMeas(); /* Display measurements on following rows */
418 
419  remainingSleeptime += RTC_getPassedSleeptime(); /* Add the time spend sleeping to the variable */
420 
421  /* Go back to sleep for the remaining time until another measurement (we only take measurements on RTC/button wake-up) */
422  sleep(WAKE_UP_PERIOD_S - remainingSleeptime);
423 
424  }
425  else
426  {
427  MCUstate = MEASURE;
428  }
429 
430  } break;
431 
432  default:
433  {
434  error(10);
435  } break;
436  }
437  }
438 }
439 
440 
441 /**************************************************************************//**
442  * @brief
443  * NMI interrupt service routine.
444  *
445  * @note
446  * The *weak* definition for this method is located in `startup_gcc_efm32hg.h`.
447  *****************************************************************************/
448 void NMI_Handler(void)
449 {
450  while(1);
451 }
452 
453 
454 /**************************************************************************//**
455  * @brief
456  * HardFault interrupt service routine.
457  *
458  * @note
459  * The *weak* definition for this method is located in `startup_gcc_efm32hg.h`.
460  *****************************************************************************/
462 {
463  while(1);
464 }
465 
466 
467 /**************************************************************************//**
468  * @brief
469  * SVC interrupt service routine.
470  *
471  * @note
472  * The *weak* definition for this method is located in `startup_gcc_efm32hg.h`.
473  *****************************************************************************/
474 void SVC_Handler(void)
475 {
476  while(1);
477 }
478 
479 
480 /**************************************************************************//**
481  * @brief
482  * PendSV interrupt service routine.
483  *
484  * @note
485  * The *weak* definition for this method is located in `startup_gcc_efm32hg.h`.
486  *****************************************************************************/
487 void PendSV_Handler(void)
488 {
489  while(1);
490 }
491 
492 
bool checkBTNinterrupts(void)
Method to check if any interrupts are triggered by buttons.
Definition: main.c:97
void initADC(ADC_Measurement_t peripheral)
Method to initialize the ADC to later check the battery voltage or internal temperature.
Definition: adc.c:87
void sendStatus(int8_t status)
Send status to the cloud using NB-IoT.
#define DBG_UART
Definition: pin_mapping.h:89
void dbprintln(char *message)
Print a string (char array) to USARTx and go to the next line.
Definition: dbprint.c:397
void dbinfoInt(char *message1, int32_t value, char *message2)
Print an info value surrounded by two strings (char array) to USARTx.
Definition: dbprint.c:569
int8_t initVL53L1X(void)
Initialize the sensor.
Definition: VL43L1X.c:69
void SVC_Handler(void)
SVC interrupt service routine.
Definition: main.c:474
int8_t clearLCD(void)
Clear the LCD.
Definition: serlcd.c:168
Definitions of the custom data-types used.
MeasurementData_t data
Definition: main.c:86
#define DBG_UART_LOC
Definition: pin_mapping.h:90
void dbwarn(char *message)
Print a warning string (char array) in yellow to USARTx and go to the next line.
Definition: dbprint.c:523
String (char array) conversion functionality.
void initBG96(void)
Initialize the GPIO pins and UART0 module necessary to communicate with the BG96 module.
void sendMeasurements(MeasurementData_t *data)
Send measurements to the cloud using NB-IoT.
Definition: adc.h:50
ADC functionality for reading the (battery) voltage and internal temperature.
uint16_t measureVL53L1X(void)
Get a sensor reading.
Definition: VL43L1X.c:160
Code for the VL53L1X sensor.
void PendSV_Handler(void)
PendSV interrupt service routine.
Definition: main.c:487
int8_t writeStringLCD(char *message)
Print a string (char array) to the LCD.
Definition: serlcd.c:264
void error(uint8_t number)
Error method.
Definition: util.c:122
serLCD functionality.
static void uint32_to_charDec(char *buf, uint32_t value)
Convert a uint32_t value to a decimal char array (string).
Definition: dbprint.c:1124
uint32_t RTC_getPassedSleeptime(void)
Method to get the time spend sleeping (in seconds) in the case of GPIO wake-up.
Definition: delay.c:456
int int32_t
Typedef defining 32 bit int type. The developer should modify this to suit the platform being deploye...
Definition: vl53l1_types.h:83
Utility functionality.
void initGPIOwakeup(void)
Initialize GPIO wake-up functionality.
Definition: interrupt.c:91
The pin definitions for the custom board.
void PULSE_setTriggered(uint8_t number, bool value)
Setter for the PULSE0/1_triggered variable.
Definition: interrupt.c:187
Delay functionality.
Interrupt functionality.
void dbprint_INIT(USART_TypeDef *pointer, uint8_t location, bool vcom, bool interrupts)
Initialize USARTx.
Definition: dbprint.c:150
void RTC_clearWakeup(void)
Method to clear RTC_sleep_wakeup.
Definition: delay.c:442
bool PULSE_getTriggered(uint8_t number)
Getter for the PULSE0/1_triggered variable.
Definition: interrupt.c:167
bool BTN_getTriggered(void)
Getter for the BTN_triggered variable.
Definition: interrupt.c:137
#define MAX_LENGTH_MM
Definition: main.c:74
Enable or disable printing to UART with dbprint.
void delay(uint32_t msDelay)
Wait for a certain amount of milliseconds in EM2/3.
Definition: delay.c:124
void displayMeas(void)
Method to display the latest measurements on the LCD.
Definition: main.c:116
int8_t setCursorLCD(uint8_t col, uint8_t row)
Set the cursor to a specific location on the LCD.
Definition: serlcd.c:316
BG96 AT-command driver functionality.
void sendPulse(uint8_t number, uint8_t pulse)
Send current pulses to the cloud using NB-IoT.
#define WAKE_UP_PERIOD_S
Definition: main.c:79
static MCU_State_t MCUstate
Definition: main.c:83
enum mcu_states MCU_State_t
int32_t readADC(ADC_Measurement_t peripheral)
Method to read the battery voltage or internal temperature.
Definition: adc.c:189
SHT30 functionality.
void HardFault_Handler(void)
HardFault interrupt service routine.
Definition: main.c:461
void BTN_setTriggered(bool value)
Setter for the BTN_triggered variable.
Definition: interrupt.c:150
Definition: datatypes.h:54
void NMI_Handler(void)
NMI interrupt service routine.
Definition: main.c:448
bool RTC_checkWakeup(void)
Method to check if the wakeup was caused by the RTC.
Definition: delay.c:432
void dbprintln_color(char *message, dbprint_color_t color)
Print a string (char array) to USARTx in a given color and go to the next line.
Definition: dbprint.c:482
void sleep(uint32_t sSleep)
Sleep for a certain amount of seconds in EM2/3.
Definition: delay.c:293
int32_t readTempDS18B20(uint8_t number)
Get a temperature value from the DS18B20.
Definition: DS18B20.c:119
int main(void)
Main function.
Definition: main.c:193
unsigned short uint16_t
Typedef defining 16 bit unsigned short type. The developer should modify this to suit the platform be...
Definition: vl53l1_types.h:88
unsigned char uint8_t
Typedef defining 8 bit unsigned char type. The developer should modify this to suit the platform bein...
Definition: vl53l1_types.h:98
unsigned int uint32_t
Typedef defining 32 bit unsigned int type. The developer should modify this to suit the platform bein...
Definition: vl53l1_types.h:78
All code for the DS18B20 temperature sensor.