如何缩小将 DHT11 用于 AVR 的代码 (Attiny45)

How to downsize code that uses DHT11 for AVR (Attiny45)

我正在尝试构建一个基于 DHT11 的小型湿度计,但我在代码大小方面遇到了一些“问题”。我想 运行 它在 Attiny45 上,它有点太大了(352 字节太大而不准确)。我知道我可以只使用 Attiny85 并有 space 备用或不使用引导加载程序并且几乎不适合它 (94%) 但我有点想让我的生活比它需要的更难并弄清楚如何减小尺寸,因为它将来可能会派上用场。如果愿意,可以将其视为一种学习经历。

它应该做什么:

旁注:7 段是通过两个 74HC595 寻址的,其中我每个使用 7 个输出用于显示器,每个使用 1 个用于将相关显示器连接到 GND 的晶体管。底下有原理图,有兴趣的可以看看。

正如所指出的,我的主要问题是代码大小,所以如果有人有任何关于如何减少它的提示(或任何其他关于如何改进代码的提示)请告诉我。

我希望我问的问题正确,如果不正确请告诉我。


编译器输出:

Sketch uses 3872 bytes (110%) of program storage space. Maximum is 3520 bytes.text section exceeds available space in board

Global variables use 107 bytes (41%) of dynamic memory, leaving 149 bytes for local variables. Maximum is 256 bytes.
Sketch too big; see https://support.arduino.cc/hc/en-us/articles/360013825179 for tips on reducing it.
Error compiling for board ATtiny45/85 (Optiboot).

代码:

/*
Humidity/Temperature sensor setup with DHT-11
Two digits for humidity
Two digits for temperature
Button to wake up from sleep
NPNs activated via 8th bit in 74HC595s, always alternating

Author:   ElectroBadger
Date:     2021-11-02
Version:  1.0
*/

/*
Reduce power consumption:
- Run at 1 MHz internal clock
- Turn off ADC
- Use SLEEP_MODE_PWR_DOWN
*/

#include "DHT.h" //DHT-11 sensor
#include <avr/sleep.h> // Sleep Modes
#include <avr/power.h> // Power management
#include <avr/wdt.h> //Doggy stuff

//define attiny pins
#define INT_PIN PB4
#define DATA PB1
#define SENSOR PB3
#define LATCH PB2
#define CLK PB0

//define other stuff
#define SENSOR_TYPE DHT11
#define LED_DELAY 50

//changing variables
short ones_data; //16-bits for display of ones
short tens_data; //16-bits for display of tens
byte sevSeg, measurements; //7-segment bit pattern / wait time between LEDs [ms] / # of measurements taken
bool firstPair, btnPress; //tracks which pair of 7-segments is on; tracks button presses
uint32_t oldMillis, sleepTimer; //tracks the last acquisition time and wakeup time

//Initialize sensor
DHT dht(SENSOR, SENSOR_TYPE);

//Shifts 16 bits out MSB first, on the rising edge of the clock.
void shiftOut(int dataPin, int clockPin, short toBeSent){
  int i=0;
  int pinState = 0;
  
  //Clear everything out just in case
  digitalWrite(dataPin, 0);
  digitalWrite(clockPin, 0);

  //Loop through bits in the data bytes, COUNTING DOWN in the for loop so that
  //0b00000000 00000001 or "1" will go through such that it will be pin Q0 that lights.
  for(i=0; i<=15; i++){
    digitalWrite(clockPin, 0);
    //if the value passed to myDataOut AND a bitmask result
    //is true then set pinState to 1
    if(toBeSent & (1<<i)){
      pinState = 1;
    }
    else{
      pinState = 0;
    }
    
    digitalWrite(dataPin, pinState); //Sets the pin to HIGH or LOW depending on pinState
    digitalWrite(clockPin, 1); //Shifts bits on upstroke of clock pin
    digitalWrite(dataPin, 0); //Zero the data pin after shift to prevent bleed through
  }
  digitalWrite(clockPin, 0); //Stop shifting
}

//Converts an int <10 to a bit pattern for 7-segment displays
short toSegments(int value){
  byte pattern = 0b00000000; //create empty pattern

  //Using a switch...case (3878 bytes) if...else if...else uses 3946 bytes
  switch(value){
    case 0:
      pattern = 0b01111110;
      break;
    case 1:
      pattern = 0b00110000;
      break;
    case 2:
      pattern = 0b01101101;
      break;
    case 3:
      pattern = 0b01111001;
      break;
    case 4:
      pattern = 0b00110011;
      break;
    case 5:
      pattern = 0b01011011;
      break;
    case 6:
      pattern = 0b01011111;
      break;
    case 7:
      pattern = 0b01110000;
      break;
    case 8:
      pattern = 0b01111111;
      break;
    case 9:
      pattern = 0b01111011;
      break;
    default:
      pattern = 0b00000000;
      break;
  }

  return pattern;
}

void goToSleep(){
  //Turn off 7-segments and NPNs
  digitalWrite(LATCH, 0);
  shiftOut(DATA, CLK, 0b0000000000000000); 
  digitalWrite(LATCH, 1);
  //Set deep sleep mode
  set_sleep_mode (SLEEP_MODE_PWR_DOWN);
  ADCSRA = 0; // turn off ADC
  power_all_disable (); // power off ADC, Timer 0 and 1, serial interface
  cli(); // timed sequence coming up, so disable interrupts
  btnPress = false;
  measurements = 0;
  resetWatchdog (); // get watchdog ready
  sleep_enable (); // ready to sleep
  sei(); // interrupts are required now
  sleep_cpu (); // sleep                
  sleep_disable (); // precaution
  power_all_enable (); // power everything back on
}
        
ISR(PCINT_VECTOR){
  btnPress = true;
  sleepTimer = millis();
}

// watchdog interrupt
ISR(WDT_vect){
  wdt_disable(); //disable watchdog
}

void resetWatchdog(){
  MCUSR = 0; //clear various "reset" flags    
  WDTCR = bit (WDCE) | bit (WDE) | bit (WDIF); //allow changes, disable reset, clear existing interrupt
  //set interrupt mode and an interval (WDE must be changed from 1 to 0 here)
  WDTCR = bit (WDIE) | bit (WDP3) | bit (WDP0); //set WDIE, and 8 seconds delay
  wdt_reset(); //pat the dog
  }
 
void setup(){
  resetWatchdog(); // do this first in case WDT fires
  cli(); //Disable interrupts during setup
  
  pinMode(INT_PIN, INPUT_PULLUP); //Set interrupt pin as input w/ internal pullup
  pinMode(DATA, OUTPUT); //Set serial data as output
  pinMode(CLK, OUTPUT); //Set shift register clock as output
  pinMode(LATCH, OUTPUT); //Set output register (latch) clock as output
  
  // Interrupts
  PCMSK = bit(INT_PIN); //Enable interrupt handler (ISR)
  GIFR  |= bit(PCIF); // clear any outstanding interrupts
  GIMSK |= bit(PCIE); //Enable PCINT interrupt in the general interrupt mask

  //default conditions
  /*  bit 0-6: ones digits
  bit 7: NPN for units digits
  bit 8-14: ones digits
  bit 15: NPN for tens digits
  */
  ones_data = 0b0000000000000000;
  tens_data = 0b0000000000000000;
  measurements = 0;
  firstPair = true;
  btnPress = false;
  oldMillis = 0;
  sleepTimer = 0;
  
  //Start sensor
  dht.begin();
  delay(1000); //wait 1s for sensor to stabilize
        
  sei(); //Enable interrupts after setup
}

void loop(){ 
  if((millis()-oldMillis) > 1000){
    //Slow sensor, so readings may be up to 2 seconds old
    byte hum = dht.readHumidity(); //Read humidity
    byte temp = dht.readTemperature(); //Read temperatuer in °C
    
    //update tens bit string
    tens_data = 0b0000000000000000; //reset to all 0s
    sevSeg = toSegments(hum/10); //convert tens of humidity to 7-segment logic
    tens_data |= sevSeg; // bitwise OR the result with the output short
    tens_data = tens_data << 8; //shift by 8 so it's almost in the right place (see below)
    sevSeg = toSegments(temp/10); //convert tens of temperature to 7-segment logic
    tens_data |= sevSeg; // bitwise OR the result with the output short
    tens_data = tens_data << 1; //shift by 1 so everything is in the right place
    tens_data |= 0b0000000100000000; //set NPN for tens pair to active and ones NPN to inactive 
    
    //update ones bit string
    ones_data = 0b0000000000000000; //reset to all 0s
    sevSeg = toSegments(hum%10); //convert ones of humidity to 7-segment logic
    ones_data |= sevSeg; // bitwise OR the result with the output short
    ones_data = ones_data << 8; //shift by 8 so it's almost in the right place (see below)
    sevSeg = toSegments(temp%10); //convert ones of temperature to 7-segment logic
    ones_data |= sevSeg; // bitwise OR the result with the output short
    ones_data = ones_data << 1; //shift by 1 so everything is in the right place
    ones_data |= 0b0000000000000001; //set NPN for ones pair to active and tens NPN to inactive 
    
    oldMillis = millis(); //I don't much care about the few ms lost
  }             //during data acquisition
  
  if(btnPress){
    //shift out the next batch of data to the display
    digitalWrite(LATCH, 0); //Set latch pin LOW so nothing gets shifted out
    if(firstPair){
      shiftOut(DATA, CLK, tens_data); //Shift out LED states for 7-segments of tens
      firstPair = false;
    }
    else{
      shiftOut(DATA, CLK, ones_data); //Shift out LED states for 7-segments of ones
      firstPair = true;
    }
    digitalWrite(LATCH, 1); //sent everything out in parallel
    delay(LED_DELAY); //wait for some time until switching to the other displays
    
    if((millis()-sleepTimer) > 6000){ //Sleep after 6s display time
      goToSleep();
    }
  }
  else{
    if(measurements > 5){
      goToSleep();
    }
  }
}

DHT11 hygrometer schematic

好的,感谢 Mat 的帮助,我尝试用更时尚的东西替换 DHT11 库,这花了我一段时间才起床 运行。我最终使用 this 作为基础,进行了一些编辑并为了我的利益发表了大量评论。 我在下面为任何感兴趣的人添加了我的更新代码(感谢指出正确的突出显示问题),还有一个 github 和其余的设计文件。

似乎这个库真的很重,正如编译器输出所示:

编译器输出:

Sketch uses 2354 bytes (66%) of program storage space. Maximum is 3520 bytes.
Global variables use 104 bytes (40%) of dynamic memory, leaving 152 bytes for local variables. Maximum is 256 bytes.

代码:

/*
  Humidity/Temperature sensor setup with DHT-11
  Two digits for humidity
  Two digits for temperature
  Button to wake up from sleep
  NPNs activated via 8th bit in 74HC595s, always alternating

  Author:   ElectroBadger
  Date:     2021-11-09
  Version:  2.0
*/

/*
  Reduce power consumption:
  - Run at 1 MHz internal clock
  - Turn off ADC
  - Use SLEEP_MODE_PWR_DOWN
*/

//#include "DHT.h"      // DHT-11 sensor
#include <avr/sleep.h>  // Sleep Modes
#include <avr/power.h>  // Power management
#include <avr/wdt.h>    // Doggy stuff

// define attiny pins
#define INT_PIN PB4
#define DATA PB1
#define SENSOR PB3
#define LATCH PB2
#define CLK PB0

// define other stuff
//#define SENSOR_TYPE DHT11
#define LED_DELAY 50

//fixed variables
//array lookup for number display; ascending order: 0, 1, 2, ...
const byte numLookup[] = {
  0b01111110, //0
  0b00110000, //1
  0b01101101, //2
  0b01111001, //3
  0b00110011, //4
  0b01011011, //5
  0b01011111, //6
  0b01110000, //7
  0b01111111, //8
  0b01111011  //9
}; 

// changing variables
short ones_data;                // 16-bits for display of ones
short tens_data;                // 16-bits for display of tens
byte sevSeg, measurements;      // 7-segment bit pattern / wait time between LEDs [ms] / # of measurements taken
bool firstPair, btnPress;       // tracks which pair of 7-segments is on; tracks button presses
uint32_t oldMillis, sleepTimer; // tracks the last acquisition time and wakeup time
byte humI, humD, tempI, tempD;  // values of humidity and temperature (we're only gonna need integral parts but I need all for the checksum)

// Initialize sensor
//DHT dht(SENSOR, SENSOR_TYPE);

// Shifts 16 bits out MSB first, on the rising edge of the clock.
void shiftOut(int dataPin, int clockPin, short toBeSent) {
  int i = 0;
  int pinState = 0;

  // Clear everything out just in case
  digitalWrite(dataPin, 0);
  digitalWrite(clockPin, 0);

  // Loop through bits in the data bytes
  for (i = 0; i <= 15; i++) {
    digitalWrite(clockPin, 0);
    // if the value AND a bitmask result is true then set pinState to 1
    if (toBeSent & (1 << i)) {
      pinState = 1;
    }
    else {
      pinState = 0;
    }

    digitalWrite(dataPin, pinState);  // sets the pin to HIGH or LOW depending on pinState
    digitalWrite(clockPin, 1);        // shifts bits on upstroke of clock pin
    digitalWrite(dataPin, 0);         // zero the data pin after shift to prevent bleed through
  }
  digitalWrite(clockPin, 0);          // Stop shifting
}

void start_signal(byte SENSOR_PIN) {
  pinMode(SENSOR_PIN, OUTPUT);        // set pin as output
  digitalWrite(SENSOR_PIN, LOW);      // set pin LOW
  delay(18);                          // wait 18 ms
  digitalWrite(SENSOR_PIN, HIGH);     // set pin HIGH
  pinMode(SENSOR_PIN, INPUT_PULLUP);  // set pin as input and pull to VCC (10k)
}

boolean read_dht11(byte SENSOR_PIN) {
  uint16_t rawHumidity = 0;
  uint16_t rawTemperature = 0;
  uint8_t checkSum = 0;
  uint16_t data = 0;

  unsigned long startTime;

  for (int8_t i = -3; i < 80; i++) {  // loop 80 iterations, representing 40 bits * 2 (HIGH + LOW)
    byte high_time;                   // stores the HIGH time of the signal
    startTime = micros();             // stores the time the data transfer started

    // sensor should pull line LOW and keep for 80µs (while SENSOR_PIN == HIGH)
    // then pull HIGH and keep for 80µs (while SENSOR_PIN == LOW)
    // then pull LOW again, aka send data (while SENSOR_PIN == HIGH)
    do {                                                  // waits for sensor to respond
      high_time = (unsigned long)(micros() - startTime);  // update HIGH time
      if (high_time > 90) {                               // times out after 90 microseconds
        Serial.println("ERROR_TIMEOUT");
        return;
      }
    }
    while (digitalRead(SENSOR_PIN) == (i & 1) ? HIGH : LOW);

    // actual data starts at iteration 0
    if (i >= 0 && (i & 1)) {  // if counter is odd, do this (only counts t_on time and ignores t_off)
      data <<= 1;             // left shift data stream by 1 since we are at a the next bit

      // TON of bit 0 is maximum 30µs and of bit 1 is at least 68µs
      if (high_time > 30) {
        data |= 1; // we got a one
      }
    }

    switch ( i ) {
      case 31:                  // bit 0-16 is humidity
        rawHumidity = data;
        break;
      case 63:                  // bit 17-32 is temperature
        rawTemperature = data;
      case 79:                  // bit 33-40 is checksum
        checkSum = data;
        data = 0;
        break;
    }
  }

  // Humidity
  humI = rawHumidity >> 8;
  rawHumidity = rawHumidity << 8;
  humD = rawHumidity >> 8;

  // Temperature
  tempI = rawTemperature >> 8;
  rawTemperature = rawTemperature << 8;
  tempD = rawTemperature >> 8;

  if ((byte)checkSum == (byte)(tempI + tempD + humI + humD)) {
    return true;
  }
  else {
    return false;
  }
}

void goToSleep() {
  // Turn off 7-segments and NPNs
  digitalWrite(LATCH, 0);
  shiftOut(DATA, CLK, 0b0000000000000000);
  digitalWrite(LATCH, 1);
  
  set_sleep_mode (SLEEP_MODE_PWR_DOWN); // Set deep sleep mode
  ADCSRA = 0;                           // turn off ADC
  power_all_disable ();                 // power off ADC, Timer 0 and 1, serial interface
  cli();                                // timed sequence coming up, so disable interrupts
  btnPress = false;                     // reset button flag
  measurements = 0;                     // reset measurement counter
  resetWatchdog ();                     // get watchdog ready
  sleep_enable ();                      // ready to sleep
  sei();                                // interrupts are required now
  sleep_cpu ();                         // sleep
  sleep_disable ();                     // precaution
  power_all_enable ();                  // power everything back on
}

ISR(PCINT0_vect) {
  btnPress = true;
  sleepTimer = millis();
}

// watchdog interrupt
ISR(WDT_vect) {
  wdt_disable(); // disable watchdog
}

void resetWatchdog() {
  MCUSR = 0;                                    //clear various "reset" flags
  WDTCR = bit (WDCE) | bit (WDE) | bit (WDIF);  //allow changes, disable reset, clear existing interrupt
  WDTCR = bit (WDIE) | bit (WDP3) | bit (WDP0); //set WDIE, and 8 seconds delay
  wdt_reset();
}

void setup() {
  resetWatchdog(); // do this first in case WDT fires
  cli(); // disable interrupts during setup

  pinMode(INT_PIN, INPUT_PULLUP); // set interrupt pin as input w/ internal pullup
  pinMode(DATA, OUTPUT);          //set serial data as output
  pinMode(CLK, OUTPUT);           //set shift register clock as output
  pinMode(LATCH, OUTPUT);         //set output register (latch) clock as output
  pinMode(SENSOR, INPUT);         //set DHT11 pin as input

  // Interrupts
  PCMSK = bit(INT_PIN);   // enable interrupt handler (ISR)
  GIFR  |= bit(PCIF);     // clear any outstanding interrupts
  GIMSK |= bit(PCIE);     // enable PCINT interrupt in the general interrupt mask

  //default conditions
  /*  bit 0-6: ones digits
    bit 7: NPN for units digits
    bit 8-14: ones digits
    bit 15: NPN for tens digits
  */
  ones_data = 0b0000000000000000;
  tens_data = 0b0000000000000000;
  measurements = 0;
  firstPair = true;
  btnPress = false;
  oldMillis = 0;
  sleepTimer = 0;
  humI = 0;
  humD = 0;
  tempI = 0;
  tempD = 0;
  
  // Start sensor
  //dht.begin();

  sei(); // enable interrupts after setup
}

void loop() {
  if ((millis() - oldMillis) > 1000) {
    // slow sensor, so readings may be up to 2 seconds old
    //byte hum = dht.readHumidity(); //Read humidity
    //byte temp = dht.readTemperature(); //Read temperatuer in °C
    delay(2000); // wait for DHT11 to start up
    start_signal(SENSOR); // send start sequence

    if(read_dht11(SENSOR)){
      // update tens bit string
      tens_data = 0b0000000000000000;     // reset to all 0s
      tens_data |= numLookup[humI / 10];  // bitwise OR the result with the output short
      tens_data = tens_data << 8;         // shift by 8 so it's almost in the right place (see below)
      tens_data |= numLookup[tempI / 10]; // bitwise OR the result with the output short
      tens_data = tens_data << 1;         // shift by 1 so everything is in the right place
      tens_data |= 0b0000000100000000;    // set NPN for tens pair to active and ones NPN to inactive
  
      // update ones bit string
      ones_data = 0b0000000000000000;     // reset to all 0s
      ones_data |= numLookup[humI % 10];  // bitwise OR the result with the output short
      ones_data = ones_data << 8;         // shift by 8 so it's almost in the right place (see below)
      ones_data |= numLookup[tempI % 10]; // bitwise OR the result with the output short
      ones_data = ones_data << 1;         // shift by 1 so everything is in the right place
      ones_data |= 0b0000000000000001;    // set NPN for ones pair to active and tens NPN to inactive
    }
    else{
      tens_data = 0b1001111110011100;
      ones_data = 0b0000101000001011;
    }
    oldMillis = millis(); // I don't much care about the few ms lost during data acquisition
  }

  if (btnPress) {
    // shift out the next batch of data to the display
    digitalWrite(LATCH, 0); // Set latch pin LOW so nothing gets shifted out
    
    if (firstPair) {
      shiftOut(DATA, CLK, tens_data); // shift out LED states for 7-segments of tens
      firstPair = false;              // reset first digit flag
    }
    else {
      shiftOut(DATA, CLK, ones_data); //Shift out LED states for 7-segments of ones
      firstPair = true;               //set first digit flag
    }
    
    digitalWrite(LATCH, 1); //sent everything out in parallel
    delay(LED_DELAY);       //wait for some time until switching to the other displays

    if ((millis() - sleepTimer) > 6000) { //Sleep after 6s display time
      goToSleep();
    }
  }
  else {
    if (measurements > 3) {
      goToSleep();
    }
  }
}