Преобразование числа с плавающей запятой/двойника в экспоненциальное представление или единицы Si

Я пытаюсь создать, казалось бы, простой фрагмент кода, который не могу заставить работать.

Он принимает двойное число (или число с плавающей запятой) и форматирует его в строку определенным образом: например. 13.453.123,25 (Точки для тысяч, запятые для дробей? десятичные дроби? обязательный английский, который не является моим родным языком и т. д.) Единицы СИ "J: 13,453M" или научные "J: 1,345E6" Оба варианта подходят, хотя я предпочитаю единицы СИ. Эта строка будет выведена на крошечный дисплей I2C с диагональю 0,96 дюйма, поэтому ее ширина должна составлять 10 символов. Я делаю джоулиметр с помощью Arduino Nano и хочу, чтобы на дисплее отображались данные об использованной в данный момент энергии. Фактически точные данные будут храниться на карте USB.

Я пробовал 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.

Из любопытства можно 1,36Е-3 миллиампера? Это сочетание научной системы обозначений с единицами СИ. Или более явно: 3,14E-5 метров?

Thomas Matthews 02.05.2024 22:20

Да. Это то же самое, что сказать 136 микроампер. Единицы СИ буквально созданы для использования в научной системе обозначений.

Delta_G 03.05.2024 00:36

Какой тип Arduino вы используете? Некоторые платы не поддерживают плавающие числа в sprintf, а некоторые поддерживают.

Delta_G 03.05.2024 00:37

@Delta_G Ардуино Нано

OADINC 03.05.2024 08:14

На нано поддержка плавающих точек отключена в sprintf. Вам придется использовать dtostre, чтобы сделать это правильно.

Delta_G 03.05.2024 18:54
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
5
85
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Это не совсем то, что я хочу, но достаточно близко и это работает!

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];
}

Другие вопросы по теме