Появляется вторая запятая после числа с запятой, кто сталкивался?

Romhik
Offline
Зарегистрирован: 11.10.2017

Здравствуйте.
В своём проекте использую Arduino Nano и АЦП HX711.
Использую числа и значения типа float.
Программа работает нормально, в терминале выводит число с двумя разрядами после запятой, например: "123.00".
Потом, через некоторое время в конце числа с запятой появляется еще одно число с запятой и еще два числа после неё, которые не меняются, например: "123.001.30", последнее значение в конце "1.30" не изменяется, хотя само число, которое в начале изменяется, то есть как бы добавляется вот этот остаток "1.30", а так всё нормально.
Если сделать сброс, то всё восстанавливается и снова показывает число с двумя знаками после запятой.
Что можно сделать, как это вылечить?

T.Rook
Offline
Зарегистрирован: 05.03.2016

Код приложите, а то как то видно плохо строки с 15 по 25

Romhik
Offline
Зарегистрирован: 11.10.2017
#include <EEPROM.h>
#include <enc28j60.h>
#include <EtherCard.h>
#include <net.h>
#include <IPAddress.h>
#include <HX711.h>
#define STATIC 0  // set to 1 to disable DHCP (adjust myip/gwip values below)

///HX711///////
HX711 scale(3, 2);                          

float calibration_factor;          // калибровка!
float units;
float ounces;
float divider;
float pluser;
//////////////

#if STATIC
// ethernet interface ip address
static byte myip[] = { 192,168,182,251 };
// gateway ip address
static byte gwip[] = { 192,168,182,111 };
static byte destip[]= {192,168,182,250};
static int myport=4220,destport=4020;
#endif
static byte mymac[] = { 0x1A,0x2B,0x3C,0x4D,0x5E,0x01 };
byte Ethernet::buffer[700];
static uint32_t timer;
static uint32_t timer1;
static byte hisip[] = { 192,168,182,250 };
static byte dnsip[] = { 192,168,182,111 };
//const char website[] PROGMEM = "192.168.182.220";
const int dstPort PROGMEM = 4020;

const int srcPort PROGMEM = 4220;

String str;
int num = 0;
char cstr[20];

String str1;
int num1 = 0;
char cstr1[20];

float calibr;

//callback that prints received packets to the serial port
void udpSerialPrint(uint16_t dest_port, uint8_t src_ip[IP_LEN], uint16_t src_port, const char *data, uint16_t len){
  IPAddress src(src_ip[0],src_ip[1],src_ip[2],src_ip[3]);

  Serial.print("dest_port: ");
  Serial.println(dest_port);
  Serial.print("src_port: ");
  Serial.println(src_port);
  
  
  Serial.print("src_ip: ");
  ether.printIp(src_ip);
  Serial.println();
  Serial.print("data: ");
  Serial.println(data);

String d1;
float d2;
d1 = strtok(data,":");
d2 = atof(strtok(NULL,":"));

float eeprom_write = d2;

if (d1 == "CALIBR") {
  EEPROM_float_write(0, eeprom_write);
  calibration_factor = EEPROM_float_read(0);
  scale.set_scale();
  scale.set_scale(calibration_factor);       //Применяем калибровку
  Serial.print("EEPROM calibration_factor: ");
  Serial.println(calibration_factor);
  }

if (d1 == "DIVIDER") {
  EEPROM_float_write(4, eeprom_write);
  divider = EEPROM_float_read(4);
  Serial.print("EEPROM divider: ");
  Serial.println(divider);
  }
if (d1 == "PLUSER") {
  EEPROM_float_write(8, eeprom_write);
  pluser = EEPROM_float_read(8);
  Serial.print("EEPROM pluser: ");
  Serial.println(pluser);
  }

if (d1 == "RESET") {digitalWrite(4, LOW);}
if (d1 == "TARE") {
  scale.tare();
  pluser=d2;
  }

}

void setup () {
digitalWrite(4, HIGH);
delay(200); 
pinMode(4, OUTPUT);
  Serial.begin(9600);

  if (ether.begin(sizeof Ethernet::buffer, mymac) == 0) 
    Serial.println( "Failed to access Ethernet controller");
#if STATIC
  ether.staticSetup(myip, gwip);
ether.copyIp(ether.dnsip, dnsip);
#else
  if (!ether.dhcpSetup())
    Serial.println(F("DHCP failed"));
#endif
ether.copyIp(ether.hisip, hisip);
  ether.printIp("IP:  ", ether.myip);
  ether.printIp("GW:  ", ether.gwip);  
  ether.printIp("DNS: ", ether.dnsip);  
  ether.printIp("SRV: ", ether.hisip);

  //register udpSerialPrint() to port 4220
  ether.udpServerListenOnPort(&udpSerialPrint, 4220);

//////HX711//////////
  scale.set_scale();
  scale.set_scale(calibration_factor);       //Применяем калибровку
////////////////////
}
char tag[] = {"WEIGHT:"};

void loop () {  
    if (millis() > timer1) {
      timer1 = millis() + 250; 
units = scale.get_units(), 10;
ounces = units * 0.035274;

float w2 = (ounces/divider)+pluser;
str1 = tag+String(w2);
str1.toCharArray(cstr1,20);

ether.sendUdp(cstr1, sizeof cstr1, 4001, ether.hisip, 4120 );
    }
ether.packetLoop(ether.packetReceive());
    if (millis() > timer) {
      timer = millis() + 1000;  
      
units = scale.get_units(), 10;
ounces = units * 0.035274;

float w1 = (ounces/divider)+pluser;
str = tag+String(w1);
str.toCharArray(cstr,20);

     ether.sendUdp(cstr, sizeof cstr, srcPort, ether.hisip, dstPort );   
  }

}///end loop

void EEPROM_float_write(int addr, float val) // запись в ЕЕПРОМ
{  
  byte *x = (byte *)&val;
  for(byte i = 0; i < 4; i++) EEPROM.write(i+addr, x[i]);
}

float EEPROM_float_read(int addr) // чтение из ЕЕПРОМ
{    
  byte x[4];
  for(byte i = 0; i < 4; i++) x[i] = EEPROM.read(i+addr);
  float *y = (float *)&x;
  return y[0];
}

 

T.Rook
Offline
Зарегистрирован: 05.03.2016

Пример "неправильного" вывода, плииз

 

Romhik
Offline
Зарегистрирован: 11.10.2017

Схема на базе этого:

Пояснение к коду:
К этому подключен АЦП HX711, конфигурируется, настраивается UDP порт на приём 4220 и два UDP порта на передачу, 4020 и 4120. IP адрес получает автоматически, также предусмотрено включение конфигурации статического адреса. При старте считываются калибровоные значения, значение деления и значение смещения. В цикле частотой 0.25 сек. измеряется вес и оправляется на IP адрес 192,168,182,250 на порт 4120  и с частотой 1 сек. отправляется на тот-же адрес на порт 4020. И слушает порт 4220, чтобы принимать команды управления. При поступлении команд, выполняются операции изменения калибровочного значения, делителя и смещения шкалы и записываются в EEPROM.

T.Rook
Offline
Зарегистрирован: 05.03.2016

Из всего сказанного, делаем смелый вывод что указанный "в терминале выводит" некое устройство, а в Serial все выводит правильно. Так? Тогда ищите в терминале.Для очистки совести проконтрольте что уходит в передачу в 141 и 154

Romhik
Offline
Зарегистрирован: 11.10.2017

T.Rook пишет:

Пример "неправильного" вывода, плииз

 

Пример неправильного вывода:
26.51.23
19.45.23
26.43.23
23.68.23
22.77.23
Вывод после сброса:
23.54
26.51
21.85
17.05
25.02

Romhik
Offline
Зарегистрирован: 11.10.2017

T.Rook пишет:

Из всего сказанного, делаем смелый вывод что указанный "в терминале выводит" некое устройство, а в Serial все выводит правильно. Тогда ищите в терминале.Для очистки совести проконтрольте что уходит в передачу в 141 и 154


Проверял данные, которые присылает по Ethernet и в терминале COM порта через USB, всё одинаково. Менял даже платы разные, эффект один и тот-же.
Ну для меня это не проблема, я на сервере всёравно это число парсю и округляю до двух чисел после запятой, пришлось это сделать. Но например у меня есть датчик на базе ESP8266, там вроде такого нет, чтобы появлялась еще одна запятая, но бывает увеличивается число разрядов после запятой, но точно не могу сказать, нужно проверить еще раз.
Ну а с ARDUINO просто уже чисто спортивный интерес возник, хотелось бы разобраться и всё выяснить, не грозит ди это еще одной проблемой, чтобы потом в самый не подходящий момент не произошел аврал.
Нужно будет поинтересоваться еще не страдают ли таким промышленные контроллеры, может быть это нормальное явление с float числами.

T.Rook
Offline
Зарегистрирован: 05.03.2016

del

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

А если в районе 50 строки вставить 

data[len]=0;

;)

T.Rook
Offline
Зарегистрирован: 05.03.2016

[q]

может быть это нормальное явление с float числами.

[/quote]

float  тут не при чем. Где-то со строками нахомутали

T.Rook
Offline
Зарегистрирован: 05.03.2016

Romhik пишет:

Проверял данные, которые присылает по Ethernet и в терминале COM порта через USB, всё одинаково.

Одинаково что? неправильный вывод?

Добавьте в 141 (154) вывод в Serial  cstr (cstr1)

Romhik
Offline
Зарегистрирован: 11.10.2017

T.Rook пишет:

Romhik пишет:

Проверял данные, которые присылает по Ethernet и в терминале COM порта через USB, всё одинаково.

Одинаково что? неправильный вывод?

Добавьте в 141 (154) вывод в Serial  cstr (cstr1)

Да, неправильный вывод со временем появляется везде и по сети и в сом порту, после сброса, всё востанавливается, начинает выводить правильно.
Вывод в сериал мне не нужен, потому, что мне нужно то, что он отсылает в сеть. Вывод в сериал я добавлял, когда таестировал логику работы, как только всё стало работать, я удалил эти строки из кода. Думаю зачем он зря будет в сериал еще выводить?
Да, я понял, только дошло, сейчас проверю. Но искажается уже в переменной w2.
Сейчас наверно переароверю все переменные наверно, выведу их все и посмотрю...

T.Rook
Offline
Зарегистрирован: 05.03.2016

Romhik пишет:

Да, неправильный вывод со временем появляется везде и по сети и в сом порту, после сброса, всё востанавливается, начинает выводить правильно.

сейчас у Вас вывод в сом только в коллбеке. А надо проверить, что именно отдаем на передачу. Хотя дело то Ваше.

Romhik
Offline
Зарегистрирован: 11.10.2017

brokly пишет:

А если в районе 50 строки вставить 

data[len]=0;

;)


Ну я не думаю, что это поможет, потому что эта функция практически не работает, за всё время когда начало искажаться число эта функция ниразу не сработала, но думаю, что попробую её вообще вырезать и посмотреть, спасибо за идею.

T.Rook
Offline
Зарегистрирован: 05.03.2016

Romhik пишет:

Но искажается уже в переменной w2.

Да ладно!!!! float не может быть с двумя дробными частями :)

sadman41
Offline
Зарегистрирован: 19.10.2016

Я бы от String избавился напрочь для начала и попробовал изобразить что-то из этих запчастей:

char buffer[20];
char tag[]={"WEIGHT:"};
float floatVar=123.456;
...
memset(buffer, 0x00, sizeof(buffer))
memcpy(buffer, tag);
...
dtostrf(floatVar,4,2,&buffer[sizeof(tag)]);
ether.sendUdp(buffer, strlen(buffer), ...);
..

PS. Правда, я float избегать пытаюсь в 98% случаев, а для конвертации псевдофлоата в char[] у меня своя функция...

Romhik
Offline
Зарегистрирован: 11.10.2017

T.Rook пишет:

сейчас у Вас вывод в сом только в коллбеке. А надо проверить, что именно отдаем на передачу. Хотя дело то Ваше.


Ну я выводил только в сериал только переменную w2 и смотрел что происходит, сравнивал с монитором который через сеть. Тоесть w2 передаётся уже искаженной.
Вторая переменная, которая отсылается раз в секунду, вроде нормально присылается, только что смотрел записи в базе данных, там нормально всё, хотя не факт, сама база данных может это число исправлять.
Так что наверно буду сейчас проверять на всех этапах и отключать по очереди функции и смотреть, что будет.

Спасибо, вы меня хоть оживили немного, а то я вообще тут немного зашился)

T.Rook
Offline
Зарегистрирован: 05.03.2016

Romhik пишет:

Спасибо, вы меня хоть оживили немного, а то я вообще тут немного зашился)

Та да, при отладке в одиночку бывает в такой тупизм попадаю... а вот если даже "чайник" рядом, начинаешь ему рассказывать и тут же все находится.

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

кто-то тебе в строке терминальный \0 затираеть

sadman41
Offline
Зарегистрирован: 19.10.2016

Да и вообще - вычисляли бы на стороне сервера свои дробные числа. Ардуине бы попроще было и вам тоже.

Romhik
Offline
Зарегистрирован: 11.10.2017

sadman41 пишет:

Да и вообще - вычисляли бы на стороне сервера свои дробные числа. Ардуине бы попроще было и вам тоже.


Вы имеете в виду на сервак отправлять RAW значение из функции scale.read()?
В принципе хорошая идея, нужно подумать.
А не подскажите scale.read() это int или float?
А как тогда получается float из RAW значения или float считывается функцией scale.get_units()?

 

sadman41
Offline
Зарегистрирован: 19.10.2016

Romhik пишет:

А не подскажите scale.read() это int или float?
А как тогда получается float из RAW значения или float считывается функцией scale.get_units()?

HX711.h:

// waits for the chip to be ready and returns a reading
long read();
...
// returns (read_average() - OFFSET), that is the current value without the tare weight; times = how many readings to do
double get_value(byte times = 1);

// returns get_value() divided by SCALE, that is the raw value divided by a value obtained via calibration
// times = how many readings to do
float get_units(byte times = 1);

 

b707
Offline
Зарегистрирован: 26.05.2017

Romhik, по-моему все дело в элементарной ошибке. Если я правильно прочитал описание, функция String.toCharArray() не добавляет к полученной строке нулевой терминатор.

Вставте на строку 141 вот такой оператор и будет вам счастье:

cstr1[str1.length()] = '\0';

 

GarryC
Offline
Зарегистрирован: 08.08.2016

Если перед неправильным выводом был правильный типа "12345.23", то b707 прав.

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

Ааааа... ну тут же суржик :) Все дружно смотрим String.c_str()

Romhik
Offline
Зарегистрирован: 11.10.2017

b707 пишет:

Romhik, по-моему все дело в элементарной ошибке. Если я правильно прочитал описание, функция String.toCharArray() не добавляет к полученной строке нулевой терминатор.
Вставте на строку 141 вот такой оператор и будет вам счастье:

cstr1[str1.length()] = '\0';


Да, похоже что так оно и есть, но я уже понял свою ошибку, что нужно перед записью в массив, его очищать, похоже что просто там остаются числа от предыдущей записи.
Еще на одном форуме только что прочитал, что с массивами такое может быть, если массив больше, чем передаваемые в него значения, то там всё что угодно може быть в конце.
Ну как минимум есть два решения. Честно говоря еще не сталкивался на практике до этого на ардуино, на компьютерах еще такой проблемы не возникало...
Ну как минимум есть два простых решения это очистка массива перед записью в него новых значений и добавление в конец данных \0

Romhik
Offline
Зарегистрирован: 11.10.2017

DetSimen пишет:

кто-то тебе в строке терминальный \0 затираеть


Ух какой злой кот!)))) Да, Вы правы!)

Romhik
Offline
Зарегистрирован: 11.10.2017

И последний вопрос, как обнулить или очистить быстро string array, то есть массив строк cstr1?
Что-то не могу найти быстрое решение пока.

sadman41
Offline
Зарегистрирован: 19.10.2016

Romhik пишет:

И последний вопрос, как обнулить или очистить быстро string array, то есть массив строк cstr1?
Что-то не могу найти быстрое решение пока.

memset(buffer, 0x00, sizeof(buffer))
 

 

Romhik
Offline
Зарегистрирован: 11.10.2017

Так пойдёт?

for( int i = 0; i < 20;  ++i ){cstr1[i]= {""};}

 

Romhik
Offline
Зарегистрирован: 11.10.2017

sadman41 пишет:

memset(buffer, 0x00, sizeof(buffer))
 


Спасибо!

b707
Offline
Зарегистрирован: 26.05.2017

Romhik пишет:

Так пойдёт?

for( int i = 0; i < 20;  ++i ){cstr1[i]= {""};}

 

Лучше все позиции в массиве забить нулями '\0'

А вообще - имея дело с символьными массивами, совершенно не обязательно их полностью чистить, достаточно записывать нулевой байт в конце.

sadman41
Offline
Зарегистрирован: 19.10.2016

b707 пишет:

А вообще - имея дело с символьными массивами, совершенно не обязательно их полностью чистить, достаточно записывать нулевой байт в конце.

Для этого нужно strlen не забыть вычислить или еще как конец отследить, а так сразу зачпекал нулями и не переживаешь на этот счет.

b707
Offline
Зарегистрирован: 26.05.2017

sadman41 пишет:

Для этого нужно strlen не забыть вычислить или еще как конец отследить, а так сразу зачпекал нулями и не переживаешь на этот счет.

А если строка пишется в массив в цикле - как у автора? На каждой итерации весь буфер заново нулями забивать? Неужели так трудно вычислить длину строки, которую только что сам и создал?  :)

Romhik
Offline
Зарегистрирован: 11.10.2017

b707 пишет:

Лучше все позиции в массиве забить нулями '\0'

А вообще - имея дело с символьными массивами, совершенно не обязательно их полностью чистить, достаточно записывать нулевой байт в конце.


В конце записываемых данных в массив или вообще в самый последний байт массива. Размер массива известен мне.
То есть, чтобы его очистить достаточно записать:
 

cstr1[20]= {'\0'};

Так?

sadman41
Offline
Зарегистрирован: 19.10.2016

b707 пишет:

А если строка пишется в массив в цикле - как у автора? На каждой итерации весь буфер заново нулями забивать? Неужели так трудно вычислить длину строки, которую только что сам и создал?  :)

Да я вообще не особо вчитывался в эту адскую смесь со String и Ethercard. stlren() все равно бежит по массиву, так же как и memset(), так что оверхед небольшой на помещение 0x00 в ячейку, а перфекционизм удовлетворен. Я и так и сяк пользуюсь. 

Romhik
Offline
Зарегистрирован: 11.10.2017

b707 пишет:

А если строка пишется в массив в цикле - как у автора? На каждой итерации весь буфер заново нулями забивать? Неужели так трудно вычислить длину строки, которую только что сам и создал?  :)

cstr1[str1.length()] = '\0';

У меня сейчас так и сделано, правда один раз выскочила вторая запятая, без подключенного АЦП, потом сбросил и всё пропало, сейчас уже долго показывает нормально.
Если всё нормально будет, то оставлю так, если снова приклеится вторая запятая, то попробую очищать весь массив.

b707
Offline
Зарегистрирован: 26.05.2017

Romhik пишет:

В конце записываемых данных в массив или вообще в самый последний байт массива. Размер массива известен мне.
То есть, чтобы его очистить достаточно записать:
 

cstr1[20]= {'\0'};

Так?

неправильно - в конце записываемых данных.

Я вам строчку уже давал.

Romhik
Offline
Зарегистрирован: 11.10.2017

b707 пишет:

неправильно - в конце записываемых данных.

Я вам строчку уже давал.


 

memset(str1, 0x00, sizeof(str1));

У меня выдаёт ошибку:
exit status 1
cannot convert 'String' to 'void*' for argument '1' to 'void* memset(void*, int, size_t)'
Наверно я не понял как этой функцией пользоваться, пойду курить мануалы...
 

sadman41
Offline
Зарегистрирован: 19.10.2016

Что-то вы то за str1 , то за cstr1 хватаетесь. 

...лучше поймите, что String class вам тут все карты путает - вы то туда, то сюда. Имхо - связались с Ethercard - используйте везде char[].

Romhik
Offline
Зарегистрирован: 11.10.2017

М-да уж), сбивают с толку все эти преобразования, да и сам себя запутал, взял строку вместо массива)))
Пора отдыхать)
Спасибо!
 

memset(str1, 0x00, sizeof(str1));

Этот код работает.
Нужно действительно всё на массив переделать, спасибо еще раз!

Romhik
Offline
Зарегистрирован: 11.10.2017

Здравствуйте, всё хорошо работает, прошли сутки, нареканий нет!
У меня остался еще один вопрос к ГУРУ, не подскажите, что означает последняя цифра "10" в этой строке:

units = scale.get_units(), 10;

Работает одинаково как с цифрой 10, так и без неё.
Заранее спасибо.

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

в том виде, как написано это означает:  "присвоить переменной units значение функции scale.get_units(). И 10." 

примерно так. 

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017
Romhik
Offline
Зарегистрирован: 11.10.2017

DetSimen пишет:

в том виде, как написано это означает:  "присвоить переменной units значение функции scale.get_units(). И 10." 

примерно так. 


Понял, значит мне "10" здесь вообще не нужно насколько я понял, потому что я взял эту строку отсюда:
 

void loop() { 

  Serial.print("Reading: ");
  
  for(int i = 0;i < 10; i ++) units =+ scale.get_units(), 10;   // усредняем показания считав 10 раз 
  units / 10;                                                   // делим на 10
   
  ounces = units * 0.035274;                                    // переводим унции в граммы              
  Serial.print(ounces);                                          // отправляем в монитор порта
  Serial.print(" grams");  
  Serial.println();

 
}

Я ничего не усредняю, потому что происходит сильная задержка из-за этого усреднения...

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

дак и в 6 строке ничего не усредняется.  Переменная units делится на 10, результат отбрасывается. 

Romhik
Offline
Зарегистрирован: 11.10.2017

DetSimen пишет:

дак и в 6 строке ничего не усредняется.  Переменная units делится на 10, результат отбрасывается. 


Тогда не понятно к чему здесь второй оператор, 10?))) Шоб был?))) Для красоты?)))

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

просто рядом стоит

усреднение вот: 

  for(int i = 0;i < 10; i ++) units =+ scale.get_units();  
  units /= 10; 

 

Romhik
Offline
Зарегистрирован: 11.10.2017

DetSimen пишет:

просто рядом стоит


И ничего не делает?
Так может его убрать?
Или пусть живёт?))

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

можно убрать

исправлю, кста

  for(int i = 0;i < 10; i ++) units += scale.get_units();
  units /= 10.0;