将 float/double 格式化为科学记数法或 Si 单位

问题描述 投票:0回答:1

我正在尝试编写一段看似简单的代码,但我无法正常工作。

它需要一个双精度(或浮点)并以特定方式将其格式化为字符串:例如

13.453.123,25
(点代表千位,逗号代表分数?小数?必修英语不是我的母语等等......) SI 单位
"J: 13,453M"
或科学
"J: 1,345E6"
两者都可以,尽管我更喜欢 SI 单位。该字符串将被放置在一个微型 I2C 0,96" 显示屏上,因此它必须有 10 个字符宽。我正在使用 Arduino Nano 制作焦耳计,我希望在显示屏上显示当前使用的能量的计数。实际准确的数据将存储在美元卡上。

我尝试了

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 <>                                                 // https://circuitdigest.com/microcontroller-projects/arduino-timer-tutorial 2023-04-13
  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 </>

如果您需要更多信息或其他任何信息,请随时询问!

编辑: 我设法让它在在线编译器上运行(是的 w3schools.com)

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 上测试它。

c++ arduino string-formatting
1个回答
0
投票

这不完全是我想要的,但足够接近并且有效!

String Fomat_Si(double Input){
  uint8_t Si_Count = 0;
  char Si[] = {' ', 'k', 'M', 'G', 'T', 'P', 'Z'};
  while (Input > 1000) {
    Si_Count++;
    Intput /= 1000;
  }
  return "J: " + String(Input) + Si[Si_Count];
}
© www.soinside.com 2019 - 2024. All rights reserved.