如何缩小使用DHT11进行AVR的代码(Attiny45)



我正试图在DHT11的基础上建造一个小型湿度计;发行";具有代码大小。我想在Attiny45上运行它,但它有点太大了(确切地说,352字节太大了(。我知道我可以只使用Attiny85,有多余的空间,或者不使用引导程序,几乎无法安装(94%(,但我有点想让我的生活变得比需要的更艰难,并想办法缩小尺寸,因为它可能在未来派上用场。如果你愿意的话,把它当作一次学习经历。

它应该做什么:

  • 读取DHT11输入
  • 在2个两位数的7段显示器上显示结果(所以我猜总共有4个7段(
  • 大部分时间都要睡觉以保存电池
  • 每8秒唤醒一次以更新传感器值(不打开显示器(
  • 按下按钮即可唤醒以显示湿度和温度值

旁注: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湿度计原理

好吧,多亏了Mat的输入,我尝试用更时尚的东西替换DHT11库,这花了我一段时间才开始运行。最后,我把它作为一个基础,进行了一些编辑,并为我的利益发表了大量评论。我在下面为任何感兴趣的人添加了我的更新代码(感谢您指出正确的突出显示问题(,还有一个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();
}
}
}