You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Arduino Nano焦耳计:将float/double格式化为SI单位或科学计数法

Arduino Nano焦耳计:能量值格式化问题

我正在基于Arduino Nano制作焦耳计,需要将float/double类型的能量值格式化为三种特定字符串格式,优先选择SI单位格式:

  • 千分位用点、小数用逗号的格式(例如 13.453.123,25)
  • SI单位格式(例如 "J: 13,453M")
  • 科学计数法格式(例如 "J: 1,345E6")

生成的字符串需要适配0.96寸I2C显示屏,宽度限制为10字符,精确数据将存储在uSD卡中。

我尝试使用dtostre()和sprintf()函数,但均出现字符串损坏问题:dtostre()可在Serial.println()中正常输出,但无法存入字符串。

现有代码

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define Pin_Enable 2
#define Pin_Red 11
#define Pin_Green 12
#define Pin_Blue 13
#define Timer_Prefill 59285   // 59285 100ms  Set Timer PreFill = 65 535 - (16*10^6 * 0.1 sec / 256)     TCNTn = 65535 - ( ( 16*10^6 x intervaltime in sec ) / Pre_Scaler Value ) =  
bool Running = false;
bool Write = false;
uint32_t Previous_Time = 0;
uint16_t Current = 0;
uint16_t Voltage = 0;
uint8_t Count = 0;
float Energy = 0;

Adafruit_SSD1306 Display(128, 64, &Wire, -1);

void setup() {
  // <> Setup Hardware Timer <>
  noInterrupts();                                                               // Turn off interrupts during adjusting settings
  TCCR1A = 0;                                                                   // Initialize the Timer
  TCCR1B = 0;
  TCNT1 = Timer_Prefill;                                                        // Prefill Timer
  TCCR1B |= (1 << CS12);                                                        // Set the pre scaler to 256
  interrupts();                                                                 // Turn on interrruptsDisplay
  // </> Setup Hardware Timer </>  

  // <> Setup Pins <>
  pinMode(Pin_Enable, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(Pin_Enable), Recording, CHANGE);
  pinMode(Pin_Red, OUTPUT);
  pinMode(Pin_Green, OUTPUT);
  pinMode(Pin_Blue, OUTPUT);
  digitalWrite(Pin_Green, HIGH);
  // </> Setup Pins </>
  

  displayString.reserve(15); // to avoid fragmenting memory when using String
  Display.begin(SSD1306_SWITCHCAPVCC, 0x3C); //0x78 / 0x3C
  delay(2000);
  Display.setTextSize(2);
  Display.setTextColor(WHITE); // text color
  Display.setCursor(0, 10);    // position to displ
  DisplayWrite("Joulemeter REDACTED");
  delay(5000);
  Serial.begin(115200);
}

void loop() {
  if(Write) {
    Add_Record();
    Write = false;
  }
}

void DisplayWrite(String text) {
  int16_t x1;
  int16_t y1;
  uint16_t width;
  uint16_t height;

  Display.getTextBounds(text, 0, 0, &x1, &y1, &width, &height);
  Display.clearDisplay(); // clear Display
  Display.setCursor((128 - width) / 2, (64 - height) / 2);
  Display.println(text); // text to Display
  Display.display();
}

void Recording(){                                                               // Gets called everytime the button changes state.
  if (Previous_Time + 250 < millis()) {                                         // Check if atleast 250ms have passed since last changed. This is to filter out switch bounces.
    Previous_Time = millis();                                                   // Store the current time to Previous_Time for the next press.
    Running = !Running;                                                         // Toggle Running.    
                                                                                
    if (Running) {                                                              // Start recording 
      noInterrupts();
      TIMSK1 |= (1 << TOIE1);                                                   // Turn on timer interrupts (Enable timer)
      TCNT1 = Timer_Prefill;                                                    // Prefill the timer
      interrupts();
      digitalWrite(Pin_Green, LOW);
      digitalWrite(Pin_Red, HIGH);                                                             
      Serial.println("Started");
    } else {                                                                    // Stop recording
      Serial.println("Stopping");
    }  
  } 
}

String MakeXLong(String Text, uint8_t Length) {
  while(Text.length() < Length) {
    Text = "0" + Text;
  }
  return Text;
}

void Add_Record(){
  Count++;
  digitalWrite(Pin_Blue, HIGH);
  Voltage = map(analogRead(A0), 0, 1023, 0, 60000);
  Current = map(analogRead(A1), 0, 1023, 0, 65000);                                
  Energy += Voltage * Current * 0.1 * 0.001 * 0.001;
    
  if (Count >= 10) {
    // <> Generate Output <>
    String Temp_A = MakeXLong(String(Voltage), 5);
    String Temp_B = MakeXLong(String(Current), 5);

    String Disp_Text = "";
    Disp_Text += "V: " + String(Temp_A[0]) + String(Temp_A[1]) + "," + String(Temp_A[2]) + String(Temp_A[3]) + String(Temp_A[4]) + " ";
    Disp_Text += "A: " + String(Temp_B[0]) + String(Temp_B[1]) + "," + String(Temp_B[2]) + String(Temp_B[3]) + String(Temp_B[4]) + " ";
    Disp_Text += "J: "; 

    // </> Generate Output </>

    // <> Display Output <>
    DisplayWrite(Disp_Text);
    Serial.println("Next");
    Serial.println("R: " + String(Voltage) + " - " + String(Current) + " - " + String(Energy));

    // </> Display Output </>
    Count = 0;
  }
  delay(100);
  digitalWrite(Pin_Blue, LOW);
}

// <> Timer Interrupt Service Routine <>
ISR(TIMER1_OVF_vect) {
  TCNT1 = Timer_Prefill;                                                        // Prefill the timer
  digitalWrite(LED_BUILTIN, digitalRead(LED_BUILTIN) ^ 1);                      // Toggle the build in led
  
  // <> Code to be executed <>
  Write = true;

  if (!Running) {
    noInterrupts();
    TIMSK1 &= !(1 << TOIE1);                                                    // Turn off timer interrupts (Disable timer)
    interrupts();
    digitalWrite(Pin_Green, HIGH);
    digitalWrite(Pin_Red, LOW);
    Serial.println("Stopped");
  }
  // </> Code to be executed </> 
}
//</> Timer Interrupt Service Routine </>

补充验证代码

我已在在线编译器中完成功能验证,以下是示例代码:

SI单位格式验证代码(概念验证)

#include <iostream>
#include <string>
using namespace std;

int main() {
  double Energy = 14999.25;
  uint8_t Si_Count = 0;
  char Si[] = {' ', 'k', 'M', 'G', 'T', 'P', 'Z'};
  while (Energy > 1000) {
    Si_Count++;
    Energy /= 1000;
  }
  string Output = to_string((int)Energy) + Si[Si_Count];
  cout << Output;
}

科学计数法格式验证代码

#include <iostream>
#include <string>
using namespace std;

int main() {
  float Energy = 14999.25;
  char Buffer [8];
  sprintf(Buffer, "%.1E", Energy);
  string Output = "J: ";
  for (int i = 0; i < 8; i++){
    Output += Buffer[i];
  }
  cout << Output;
  return 0;
}

我将在今晚测试这些代码在Arduino硬件上的运行情况。


内容的提问来源于stack exchange,提问作者OADINC

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 17:39:50