Я пытаюсь создать, казалось бы, простой фрагмент кода, который не могу заставить работать.
Он принимает двойное число (или число с плавающей запятой) и форматирует его в строку определенным образом: например. 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.
Да. Это то же самое, что сказать 136 микроампер. Единицы СИ буквально созданы для использования в научной системе обозначений.
Какой тип Arduino вы используете? Некоторые платы не поддерживают плавающие числа в sprintf, а некоторые поддерживают.
@Delta_G Ардуино Нано
На нано поддержка плавающих точек отключена в sprintf. Вам придется использовать dtostre, чтобы сделать это правильно.
Это не совсем то, что я хочу, но достаточно близко и это работает!
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];
}
Из любопытства можно 1,36Е-3 миллиампера? Это сочетание научной системы обозначений с единицами СИ. Или более явно: 3,14E-5 метров?