Advertisement
Guest User

Untitled

a guest
Mar 24th, 2023
132
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 14.79 KB | Help | 0 0
  1. #include <Arduino.h>
  2. #include <STM32RTC.h>
  3. #include <rtc.h>
  4. #include <STM32LowPower.h>
  5. #include <low_power.h>
  6.  
  7. /*
  8. rationale
  9. 30 bimetallic switches and three thermistors will be connected to a BluePill to record the temperature at which each bimetallic switch changes state (the project will be placed in a hot or cold place to trigger the switches)
  10. plan
  11. check and store switch states in an array on setup
  12. every second, check each switch state in turn and if different from the value in the array, change the value in the array and record the temperature for each thermistor in separate arrays
  13. when all thermistors have changed state, start speaker
  14. when button is pressed, stop speaker and store temperatures in new array positions for the next stage
  15. every second, check each switch state in turn and if different from the value in the array, change the value in the array and record the temperature for each thermistor in separate arrays
  16. when all thermistors have changed state, start speaker
  17. when button is pressed, stop speaker and begin serial data transfer
  18. */
  19. #define DEBUG 1   // value is 1 to enable debugging serial printing
  20. #define DEBUG2 0  // value is 1 to enable debugging serial printing
  21.  
  22. #if DEBUG == 1
  23. #define DEBUG(x) Serial.print(x)
  24. #define DEBUGLN(x) Serial.println(x)
  25. #else
  26. #define DEBUG(x)
  27. #define DEBUGLN(x)
  28. #endif
  29.  
  30. #if DEBUG2 == 1
  31. #define DEBUG2(x) Serial.print(x)
  32. #define DEBUGLN2(x) Serial.println(x)
  33. #else
  34. #define DEBUG2(x)
  35. #define DEBUGLN2(x)
  36. #endif
  37.  
  38.  
  39.  
  40. // how many bimetallic switches to monitor simultaneously - might not use this, actually
  41. #define NUMBEROFBIMETALLICSWITCHES 30
  42.  
  43. // which analog pins to use with thermistors
  44. #define THERMISTORPIN1 PA7  //
  45. #define THERMISTORPIN2 PB0  //
  46. #define THERMISTORPIN3 PB1  //
  47.  
  48. // which digital pins to use for BUTTONPIN to trigger sending contents of matrix to serial
  49. #define BUTTONPIN PB2  //
  50. // buzzer pin
  51. #define PIEZOPIN PA6  //
  52. // resistance at 25 degrees C
  53. #define THERMISTORNOMINAL 10000
  54. // temp. for nominal resistance (almost always 25 C)
  55. #define TEMPERATURENOMINAL 25
  56. // how many samples to take and average, more takes longer
  57. // but is more 'smooth'
  58. #define NUMSAMPLES 5
  59. // The beta coefficient of the thermistor (usually 1000-4000)
  60. #define BCOEFFICIENT 3950
  61. // the value of the resistor used to make a potential divider with the thermistor
  62. #define SERIESRESISTOR 9940
  63.  
  64. // an array is need to store the pins to which each bimetallic switch will be attached
  65. unsigned char bimetallicSwitchMonitoringPins[NUMBEROFBIMETALLICSWITCHES] = { PB12, PB13, PB14, PB15, PA8, PA9, PA10, PA11, PA12, PA15, PB3, PB4, PB5, PB6, PB7, PB8, PB9, PA13, PA14, PC13, PC14, PC15, PA0, PA1, PA2, PA3, PA4, PA5, PB10, PB11 };  // commented out the pins needed for serial and swd programming
  66.  
  67. bool initialBimetallicSwitchState;
  68.  
  69. // an array to store the state of each bimetallic switch
  70. bool bimetallicSwitchStates[NUMBEROFBIMETALLICSWITCHES];
  71.  
  72. // places to store temperatures temporarily
  73. int temperature1;
  74. int temperature2;
  75. int temperature3;
  76.  
  77. // places to store temperatures after detecting change in switch state
  78. int temperatureArray1[60];
  79. int temperatureArray2[60];
  80. int temperatureArray3[60];
  81.  
  82. // arrays to store thermistor readings prior to conversion to an average temperature
  83. int samples1[NUMSAMPLES];
  84. int samples2[NUMSAMPLES];
  85. int samples3[NUMSAMPLES];
  86.  
  87. // how frequently to check the bimetallic switch states and act on readings
  88. const unsigned long temperatureMeasurementInterval = 1000;
  89. const unsigned long thermistorReadShortInterval = 10;
  90.  
  91. // how long button should be pressed
  92. const int SHORT_PRESS_TIME = 1000;  // 1000 milliseconds
  93. const int LONG_PRESS_TIME = 1000;   // 1000 milliseconds
  94.  
  95. bool bimetallicSwitchStateCheckingFlag = false;
  96. bool thermistorSamplingFlag = false;
  97. bool userInterventionRequiredFlag = false;
  98. bool thermistorSamplingCompleteFlag = false;
  99. bool phaseOneCompleteFlag = false;
  100. bool phaseTwoCompleteFlag = false;
  101. bool readyToUploadFlag = false;
  102.  
  103. bool lastButtonState;
  104. bool currentButtonState;
  105. bool buttonIsBeingPressed;
  106. bool shortButtonPressDetected;
  107. bool longButtonPressDetected;
  108. unsigned long buttonPressTimeStart;
  109. unsigned long buttonPressTimeEnd;
  110.  
  111. int loopcounter = 0;
  112.  
  113. //I have to declare functions before they're called because apparently that's how C++ works? What a waste of time
  114. void buttonPressDetection(const unsigned char BUTTON_PIN, bool &lastButtonState, bool &currentButtonState, unsigned long &buttonPressTimeStart, unsigned long &buttonPressTimeEnd, bool &buttonIsBeingPressed, bool &shortButtonPressDetected, bool &longButtonPressDetected);
  115. void userInterventionAttractionTask();
  116. void thermistorSamplingTimerTask();
  117. void thermistorSamplingTask();
  118. void thermistorSampleAveragingTask();
  119. void bimetallicSwitchStateCheckingTask();
  120. void phaseFlagSettingTask();
  121. void powerSavingTask();
  122. void serialDataTransferTask();
  123. unsigned char samplesToTemp(int samplesSensor[]);
  124.  
  125. void setup() {
  126.   // Configure low power
  127.   LowPower.begin();
  128.   Serial.begin(9600);
  129.   pinMode(BUTTONPIN, INPUT);  // have to use INPUT with an external pullup resistor, not INPUT_PULLUP because PB2 on the BluePill devboard has a 100K resistor between it and the header pins
  130.   pinMode(PIEZOPIN, OUTPUT);
  131.   tone(PIEZOPIN, 800, 1);                                              // makes a blip noise with the piezo at power-up
  132.  
  133.   // set each pin in bimetallicSwitch monitoring array to INPUT_PULLUP, then fill each position in the array with the initial state of each pin
  134.   for (unsigned char i = 0; i < NUMBEROFBIMETALLICSWITCHES; i++) {
  135.     pinMode(bimetallicSwitchMonitoringPins[i], INPUT_PULLUP);
  136.     bimetallicSwitchStates[i] = digitalRead(bimetallicSwitchMonitoringPins[i]);
  137.   }
  138.   // check that all of the bimetallic switches are in the same state and if they are not, attract user intervention
  139.   for (unsigned char i = 1; i < NUMBEROFBIMETALLICSWITCHES; i++) {
  140.     if (bimetallicSwitchStates[0] != bimetallicSwitchStates[i]) {
  141.       userInterventionRequiredFlag = true;
  142.       return;
  143.     }
  144.     initialBimetallicSwitchState = bimetallicSwitchStates[0];
  145.   }
  146. }
  147.  
  148. void loop() {
  149.   buttonPressDetection(BUTTONPIN, lastButtonState, currentButtonState, buttonPressTimeStart, buttonPressTimeEnd, buttonIsBeingPressed, shortButtonPressDetected, longButtonPressDetected);
  150.   userInterventionAttractionTask();
  151.   thermistorSamplingTimerTask();
  152.   thermistorSamplingTask();
  153.   thermistorSampleAveragingTask();
  154.   bimetallicSwitchStateCheckingTask();
  155.   phaseFlagSettingTask();
  156.   powerSavingTask();
  157.   serialDataTransferTask();
  158.   loopcounter++;
  159. }
  160.  
  161. void powerSavingTask() {
  162.   if (userInterventionRequiredFlag == true || readyToUploadFlag == true) {
  163.     return;
  164.   }
  165.   LowPower.sleep(200);
  166. }
  167.  
  168. void userInterventionAttractionTask() {
  169.   if (userInterventionRequiredFlag == false) {
  170.     return;
  171.   }
  172.   static unsigned long previousMillis = 0;
  173.   unsigned long currentMillis = millis();
  174.   if (currentMillis - previousMillis > 1200) {
  175.     previousMillis = currentMillis;
  176.     tone(PIEZOPIN, 800, 600);
  177.   }
  178.   if (shortButtonPressDetected == true) {
  179.     noTone(PIEZOPIN);
  180.     userInterventionRequiredFlag = false;
  181.   }
  182.   if (shortButtonPressDetected == true && phaseTwoCompleteFlag == true) {
  183.     readyToUploadFlag = true;
  184.   }
  185. }
  186.  
  187. void thermistorSamplingTimerTask() {
  188.   static unsigned long previousMillis = 0;
  189.  
  190.   unsigned long currentMillis = millis();
  191.   if (currentMillis - previousMillis < temperatureMeasurementInterval) {
  192.     return;
  193.   }
  194.   previousMillis = currentMillis;
  195.   thermistorSamplingFlag = true;
  196.   for (unsigned char i = 0; i < NUMBEROFBIMETALLICSWITCHES; i++) {
  197.     DEBUG2((String)bimetallicSwitchMonitoringPins[i] + "=" + bimetallicSwitchStates[i] + ",");
  198.   }
  199.   DEBUGLN2((String) "initialState:" + initialBimetallicSwitchState + ", phaseOne:" + phaseOneCompleteFlag + ", phaseTwo:" + phaseTwoCompleteFlag + ", intervention:" + userInterventionRequiredFlag + ", buttonState:" + currentButtonState + ", shortButton:" + shortButtonPressDetected);
  200.   return;
  201. }
  202.  
  203. void bimetallicSwitchStateCheckingTask() {
  204.   if (bimetallicSwitchStateCheckingFlag == false || userInterventionRequiredFlag == true || phaseTwoCompleteFlag == true) {
  205.     return;
  206.   }
  207.   for (unsigned char i = 0; i < NUMBEROFBIMETALLICSWITCHES; i++) {
  208.     if (digitalRead(bimetallicSwitchMonitoringPins[i]) != bimetallicSwitchStates[i]) {            // check whether the current state of each bitmetallic switch is different from its corresponding state stored in the array
  209.       bimetallicSwitchStates[i] = !bimetallicSwitchStates[i];                                     // set the bimetallic switch state to its new state
  210.       tone(PIEZOPIN, 800, 1);                                                                   // makes a blip noise with the piezo every time a bimetallic switch changes state
  211.       temperatureArray1[i + (NUMBEROFBIMETALLICSWITCHES * phaseOneCompleteFlag)] = temperature1;  // fill the relevant arrays with the last-recorded temperatures
  212.       temperatureArray2[i + (NUMBEROFBIMETALLICSWITCHES * phaseOneCompleteFlag)] = temperature2;
  213.       temperatureArray3[i + (NUMBEROFBIMETALLICSWITCHES * phaseOneCompleteFlag)] = temperature3;
  214.     }
  215.   }
  216. }
  217.  
  218. void phaseFlagSettingTask() {
  219.   if (readyToUploadFlag == true) {
  220.     return;
  221.   }
  222.   unsigned char i = 0;
  223.   while (i++ < NUMBEROFBIMETALLICSWITCHES - 1 && bimetallicSwitchStates[i] == bimetallicSwitchStates[0] && bimetallicSwitchStates[0] != initialBimetallicSwitchState)
  224.     ;
  225.   if (i == NUMBEROFBIMETALLICSWITCHES && phaseOneCompleteFlag == false) {
  226.     phaseOneCompleteFlag = true;
  227.     userInterventionRequiredFlag = true;
  228.     initialBimetallicSwitchState = bimetallicSwitchStates[0];
  229.   } else if (i == NUMBEROFBIMETALLICSWITCHES && phaseOneCompleteFlag == true) {
  230.     phaseTwoCompleteFlag = true;
  231.     userInterventionRequiredFlag = true;
  232.   }
  233. }
  234.  
  235.  
  236. void thermistorSamplingTask() {
  237.   static unsigned long previousMillis = 0;
  238.   static unsigned char index = 0;
  239.  
  240.   if (thermistorSamplingFlag == false) {
  241.     return;
  242.   }
  243.  
  244.   unsigned long currentMillis = millis();
  245.   // at the "thermistorReadInterval
  246.   if (millis() - previousMillis < thermistorReadShortInterval) {
  247.     return;
  248.   }
  249.   previousMillis = millis();
  250.  
  251.   // take NUMSAMPLES samples
  252.   samples1[index] = analogRead(THERMISTORPIN1);
  253.   samples2[index] = analogRead(THERMISTORPIN2);
  254.   samples3[index] = analogRead(THERMISTORPIN3);
  255.   index = (index + 1) % NUMSAMPLES;  // this resets index to 0 when array contains NUMSAMPLES values
  256.  
  257.   if (index == 0) {                         // when index is reset to 0, it's because the array is full and complete
  258.     thermistorSamplingFlag = false;         // sampling can now stop for a while
  259.     thermistorSamplingCompleteFlag = true;  // this lets the next stage of the main loop begin
  260.     return;
  261.   }
  262. }
  263.  
  264. void thermistorSampleAveragingTask() {
  265.   // check whether array is full - this flag is set to true when the array is full
  266.   if (thermistorSamplingCompleteFlag == false) {
  267.     return;
  268.   }
  269.   // do the averaging here
  270.   temperature1 = samplesToTemp(samples1);
  271.   DEBUG(", 1: ");
  272.   DEBUG(temperature1);
  273.   DEBUGLN("°C");
  274.   temperature2 = samplesToTemp(samples2);
  275.   DEBUG(", 2: ");
  276.   DEBUG(temperature2);
  277.   DEBUGLN("°C");
  278.   temperature3 = samplesToTemp(samples3);
  279.   DEBUG(", 3: ");
  280.   DEBUG(temperature3);
  281.   DEBUGLN("°C");
  282.  
  283.   thermistorSamplingCompleteFlag = false;    // reset this flag so sampling can begin again
  284.   bimetallicSwitchStateCheckingFlag = true;  // this flag lets the bimetallic switch checking start
  285. }
  286.  
  287. // function for turning list of resistance measurements into temperature reading
  288. unsigned char samplesToTemp(int samplesSensor[]) {
  289.   float average = 0;
  290.  
  291.   // average all the samples out
  292.   for (unsigned char index = 0; index < NUMSAMPLES; index++) {
  293.     average += samplesSensor[index];
  294.   }
  295.   average /= NUMSAMPLES;
  296.  
  297.   DEBUG("Average analog reading ");
  298.   DEBUG(average);
  299.  
  300.   // convert the value to resistance
  301.   average = 1023 / average - 1;  // value 4095 is for 12-bit ADC; 1023 for a 8-bit ADC
  302.   average = SERIESRESISTOR / average;
  303.   DEBUG(", Thermistor resistance ");
  304.   DEBUG(average);
  305.  
  306.   float steinhart;
  307.   steinhart = average / THERMISTORNOMINAL;           // (R/Ro)
  308.   steinhart = log(steinhart);                        // ln(R/Ro)
  309.   steinhart /= BCOEFFICIENT;                         // 1/B * ln(R/Ro)
  310.   steinhart += 1.0 / (TEMPERATURENOMINAL + 273.15);  // + (1/To)
  311.   steinhart = 1.0 / steinhart;                       // Invert
  312.   steinhart -= 273.15;                               // convert absolute temp to C
  313.  
  314.   DEBUG(", Temperature ");
  315.   DEBUG(steinhart);
  316.   DEBUG(" °C");
  317.   return steinhart + 0.5;  // adding 0.5 prevents it always rounding down when turning float to int
  318. }
  319.  
  320. void serialDataTransferTask() {
  321.   static unsigned char i;
  322.   if (phaseTwoCompleteFlag == true && userInterventionRequiredFlag == false && longButtonPressDetected == true) {
  323.     Serial.println((String) "Switch\tTemp1\tTemp2\tTemp3\tAverage123\tTemp4\tTemp5\tTemp6\tAverage456");
  324.     for (i = 0; i < NUMBEROFBIMETALLICSWITCHES; i++) {
  325.       Serial.println((String)(i + 1) + "\t" + temperatureArray1[i] + "\t" + temperatureArray2[i] + "\t" + temperatureArray3[i] + "\t=AVERAGE(B" + (i + 2) + ":D" + (i + 2) + ")\t" + temperatureArray1[i + NUMBEROFBIMETALLICSWITCHES] + "\t" + temperatureArray2[i + NUMBEROFBIMETALLICSWITCHES] + "\t" + temperatureArray3[i + NUMBEROFBIMETALLICSWITCHES] + "\t=AVERAGE(F" + (i + 2) + ":H" + (i + 2) + ")");
  326.     }
  327.     delay(3000);
  328.   }
  329. }
  330.  
  331. void buttonPressDetection(const unsigned char BUTTON_PIN, bool &lastButtonState, bool &currentButtonState, unsigned long &buttonPressTimeStart, unsigned long &buttonPressTimeEnd, bool &buttonIsBeingPressed, bool &shortButtonPressDetected, bool &longButtonPressDetected) {
  332.   shortButtonPressDetected = false;  // there might be a better place to put this but it seems to work here
  333.                                      // read the state of the switch/button:
  334.   currentButtonState = digitalRead(BUTTON_PIN);
  335.  
  336.   if (lastButtonState == HIGH && currentButtonState == LOW) {  // button is pressed
  337.     buttonPressTimeStart = millis();
  338.     buttonIsBeingPressed = true;
  339.     longButtonPressDetected = false;
  340.   } else if (lastButtonState == LOW && currentButtonState == HIGH) {  // button is released
  341.     buttonIsBeingPressed = false;
  342.     buttonPressTimeEnd = millis();
  343.  
  344.     long pressDuration = buttonPressTimeEnd - buttonPressTimeStart;
  345.  
  346.     if (10 < pressDuration && pressDuration < SHORT_PRESS_TIME) {
  347.       DEBUG("A short press is detected");
  348.       shortButtonPressDetected = true;
  349.     }
  350.     longButtonPressDetected = false;
  351.   }
  352.  
  353.   if (buttonIsBeingPressed == true && longButtonPressDetected == false) {
  354.     long pressDuration = millis() - buttonPressTimeStart;
  355.  
  356.     if (pressDuration > LONG_PRESS_TIME) {
  357.       DEBUG("A long press is detected");
  358.       longButtonPressDetected = true;
  359.     }
  360.   }
  361.  
  362.   // save the last state
  363.   lastButtonState = currentButtonState;
  364. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement