Permalänk
Medlem

arduino i2c skicka tal

Hej jag skulle vilja flyta ett tal typ ”100.55” med data typ av float en från en arduino till annan arduino med i2c
Har förstått att det inte går att flytta data typen float utan behöver få över den till text data typ
Men hur gör jag det?
så att det funkar med i2c

Permalänk
Medlem

Med I2C kan du skicka råa bytes, du behöver bara veta hur du ska serialisera ditt flyttal, och sedan göra det till ett flyttal igen när du tar emot det. Värt att tänka på är att vara försiktig med att båda systemen använder samma modell för ett flyttal. Finns en hel del olika bibliotek för att serialisera / deserialisera data.

En snabb goolgling visade tex detta: https://stackoverflow.com/questions/21005845/how-to-get-float...

Permalänk
Medlem

Går alltid att plocka isär floaten till en array av bytes, skicka dem separat och sen rekonstruera floaten hos mottagaren. Något åt det här hållet:

union {
float32_t mFloat;
unsigned char byteArr[4];
} floatbytes;

floatbytes.mFloat = DITT_TAL;

// Skicka
Wire.write( floatbytes.byteArr, 4);

På mottagarsidan läser du in de fyra byten i en array och gör tvärt om. Se till att endianess passar.

Permalänk
Medlem

@KSC:
hum din kod gillar inte min arduino
på raden med texten
float32_t mFloat
så får jag svar
'float32_t' does not name a type

någon som vet varför?

Permalänk
Hedersmedlem
Skrivet av jost1:

@KSC:
hum din kod gillar inte min arduino
på raden med texten
float32_t mFloat
så får jag svar
'float32_t' does not name a type

någon som vet varför?

Testa med bara float istället.

float mFloat;

Permalänk
Medlem

jag har testa och sökt lite på andra ställen och kommit fram till att det ska se ut så här

typedef union { float number; uint8_t bytes[4]; } test_t;

vill också tacka för hjälpen

Permalänk
Datavetare
Skrivet av jost1:

jag har testa och sökt lite på andra ställen och kommit fram till att det ska se ut så här

typedef union { float number; uint8_t bytes[4]; } test_t;

vill också tacka för hjälpen

Det du har nu kommer i praktiken fungera, i alla fall på Arduino UNO och andra AVR-baserade system.

Men det är ingen superbra idé då det är odefinierat beteende. C standarden säger explicit att enda garantin för en union är att man kan läsa den senaste medlem som skrevs, men det är odefinierat vad som händer om man skriver till A ("number" i ditt fall) och läser B ("bytes" i ditt fall).

Detta kommer alltid fungera mellan två enheter med samma "endianness" (men det är fortfarande icke-portabel kod)

float aFloatNumber = SOME_FLOAT_NUMBER; Wire.write((uint8_t *)&aFloatNumber, sizeof aFloatNumber);

Problemet är som sagt att detta förutsätter att båda sidor är endera "little endian" eller att båda är "big-endian".

Det är dock inte odefinierat då (uint8_t *) garanterat har lika eller mindre restriktion på vilka adresser som är OK jämfört med (float *).

Ett portabelt sätt vore att göra om det hela till en sträng, skicka strängen och läsa tillbaka den i en float igen.

Visa signatur

Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer

Permalänk
Medlem
Skrivet av Yoshman:

Det du har nu kommer i praktiken fungera, i alla fall på Arduino UNO och andra AVR-baserade system.

Men det är ingen superbra idé då det är odefinierat beteende. C standarden säger explicit att enda garantin för en union är att man kan läsa den senaste medlem som skrevs, men det är odefinierat vad som händer om man skriver till A ("number" i ditt fall) och läser B ("bytes" i ditt fall).

Detta kommer alltid fungera mellan två enheter med samma "endianness" (men det är fortfarande icke-portabel kod)

float aFloatNumber = SOME_FLOAT_NUMBER; Wire.write((uint8_t *)&aFloatNumber, sizeof aFloatNumber);

Problemet är som sagt att detta förutsätter att båda sidor är endera "little endian" eller att båda är "big-endian".

Det är dock inte odefinierat då (uint8_t *) garanterat har lika eller mindre restriktion på vilka adresser som är OK jämfört med (float *).

Ett portabelt sätt vore att göra om det hela till en sträng, skicka strängen och läsa tillbaka den i en float igen.

och hur löser du det så att Arduino förstår det?
så hadde jag tänk göra från början men jag hittar ingen lösning på hur man kan göra det med en Arduino

Skickades från m.sweclockers.com

Permalänk
Datavetare
Skrivet av jost1:

och hur löser du det så att Arduino förstår det?
så hadde jag tänk göra från början men jag hittar ingen lösning på hur man kan göra det med en Arduino

Skickades från m.sweclockers.com

Har inte testat detta, orkar har packat ner mina Arduino-prylar för tillfället, men detta borde fungera.

"Tricket" här är att alltid skicka data i med en specifik byte-ordning över I2C. Har valt litte-endian då AVR (som sitter i t.ex. UNO, DUO m.fl.) är little-endian.

Sändare

#include <Wire.h> #define I2C_ADDR 0x17 float someFloat = 0; void setup() { Wire.begin(); } // Converts a float storage location to little endian const uint8_t *floatToLe (float fpNum) { static float leStorage; static uint8_t *littleEndian = (uint8_t *)&leStorage; const uint8_t *hostEndian = (uint8_t *)&fpNum; #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ for (int i = 0; i < sizeof (float); i++) { littleEndian[i] = hostEndian[i]; } #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ for (int i = 0; i < sizeof (float); i++) { littleEndian[i] = hostEndian[sizeof (float) - 1 - i]; } #else #error "Cannot figure out endianness of this target" #endif return littleEndian; } void loop() { someFloat += 1.1f; Wire.beginTransmission(I2C_ADDR); Wire.write(floatToLe(someFloat), sizeof someFloat); Wire.endTransmission(); delay (500); }

Mottagare

#include <Wire.h> #define I2C_ADDR 0x17 // Little endian byte sequence to a float float leToFloat (const uint8_t * leFloat) { float fpStorage; uint8_t *fpPtr = (uint8_t *)&fpStorage; #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ for (int i = 0; i < sizeof(float); i++) { fpPtr[i] = leFloat[i]; } #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ for (int i = 0; i < sizeof(float); i++) { fpPtr[i] = leFloat[sizeof(float) - 1 - i]; } #else #error "Cannot figure out endianness of this target" #endif return fpStorage; } void receiveFloat(int bytesRecv) { float fpNum; uint8_t buf[sizeof(float)]; if (bytesRecv == sizeof(float)) { for (int i; i < sizeof(float); i++) { buf[i] = Wire.read(); } fpNum = leToFloat(buf); // Do something with "fpNum"... } } void setup() { Wire.begin(I2C_ADDR); Wire.onReceive(receiveFloat); } void loop() { delay(500); }

Visa signatur

Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer

Permalänk
Medlem

@jost1:

Anledningen till att jag körde med float32_t istället för float är för att float32_t alltid kommer att använda 4 bytes oavsett av system/kompilator och så vidare, vilket inte är garanterat för vanliga float (även om det antagligen funkar ändå). Om det funkar som det ska i ditt fall (och du inte bygger något urviktigt som ska portas eller underhållas) så spelar det ingen större roll. Testa att importera stdint.h, borde kunna köras då. Dock så borde du kunna använda float32_t om du kommer åt uint8_t tycker jag.

Permalänk
Datavetare

Tänkte lägga till en check att __STDC_IEC_559__ var definierad i koden ovan, det då float och double i praktiken alltid brukar vara IEEE 754.

Om __STDC_IEC_559__ är definierad vet man att float är 32-bitar med 23 bitars mantissa samt att double är 64-bitar med 52 mantissa.

Var inte så enkelt och visar varför de flesta skyr flyttal som pesten när det handlar om inbyggda-system och små enkla CPUer. Naturligtvis kör AVR något eget ihopkok vad det gäller representationen av flyttal.

Kollade bara att sizeof(float) var fyra (vilket det är på i princip alla "vanliga" CPUer) och så är fallet. Men det är inte samma binärrepresentation som för t.ex. x86 och ARM.

Vet man att båda sidor är en AVR finns ingen anledning att ens bry sig om endianness, det kommer vara samma (little endian). Vill man verkligen vara portabel är det nog ändå bäst att konvertera sitt flyttal till en sträng, skicka strängen och konvertera tillbaka den på andra sidan.

Det är inte heller speciellt lätt på AVR/Arduino. Konvertering från sträng till float är lätt, använd strtod().

Andra håller är värre, sprintf() och liknande verkar i normalfallet sakna stöd för flyttal då det tar för mycket utrymme.

Vet man den ungefärliga spännvidden för sina flyttal är nog enklaste sättet att först konvertera heltalsdelen med itoa(). Lätt till en decimalseparerar (punkt eller komma beroende på språk). Sist kapar man heltalsdelen och multiplicerar med tio gånger antal decimaler man vill ha, konverterar till heltal och använder itoa() igen. Nu har man ett flyttal som en sträng.

Kort och gott: det är ett rätt djupt kaninhål du har hoppat ner i här @jost1

Visa signatur

Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer

Permalänk
Medlem

hum nu är jag på rikligt djup vatten. för det första så är jag bara hobby programera har hållit på med gw basic och Visual basic, och sen gick jag styr och regler på gymnasiet och höll på med syra saker typ värmesystem. så mina kunskaper programmering inte den bästa. Nu håller jag på att göra en styrning till min pelletspanna som inte hade någon styrning därför mäter jag antal grader på vatten och luft för att den ska göra smarta beslut. ljust nu fungera den skaplig kommer att ansluta solpanel som är vattenburen och en el patron.
detta visar jag på en internet sida där jag visar hur varmt jag har i vatten taken och i pannan och hur varmt det är ute samt rökgasen. där kan jag också sätta i gång pannan och pumpar. hårdvaran består av Ethernet-sheld på min arduino. det som jag tro händer i dag är att efter ett antal data överförning till hemsidan så låser sig Ethernet-sheld och inte kan visa vad som händer i bland har den också lyckas låsa arduino. det verka finnas ett max antal innan det händer vet ej hur mång. jag hade tänkt att ha en arduino som gör styrningen av pannan och en som gör internet sida så att det blir mer driftsäkrare dessutom så kan den som styr starta om den andra. jag hade då tänkt mig använda i2c för att flyta data från ena till den andra. det den som har hand internet sida ska inte gör några beräkningar på talen så vilken format den kommer in spelar ingen roll för den ska bara skicka vidare värdet till internet sida . Det är vad jag vill göra.

om jag nu förstår rätt så kan den vid olika sätt beräkna om fel. så att värdet inte blir det samma som den sicka eller har jag fatta fel?
på tanke på vad jag ska göra vilket är då smartas att göra? eller är alla sätt rätt men det handlar mer om hur man ska skriva kod?
värdet på talen handlar om från ca -25.00 till +400.00
men om jag nu skulle ta bort decimalerna så skulle det vara lättare? inte för att jag vill det

Permalänk
Medlem

Jag hade valt att representera talet som ett heltal dvs gjort om ex. 1,84 till 184 och sedan dividerat med 100 för att få ett flyttal på andra sidan igen. Tycker det blir betydligt enklare så.

Permalänk
Medlem

jag skulle vilja läsa lite mer om hur detta sitter i hopp är det någon som kan tipsa om en bok/websida där det står något om detta?
är det lättare att skicka heltal oset hur stora det är?

Permalänk
Medlem

är det någon som kan ge tipps om någon bok eller websida där man kan läsa hur detta hänger i hopp?
är det lättare att sicka hel tal oset hur stora det är? 30055(300,55) kan väl inte få plats i en bytes eller?
och hur skickar man två värden typ 10 och 12.
man har x=10
och y=12
hur skriver man korden så att master för över dessa två tal?

Permalänk
Medlem

vill bara visa hur jag löse detta
texten i den som ska vara slav

#include <Wire.h> typedef union { float number; uint8_t bytes[4]; } test_t; int var1=2; test_t tal1; test_t tal2; int tal3=20; void setup() { Wire.begin(8); // join i2c bus with address #8 Wire.onRequest(requestEvent); // register event tal1.number =2.00; tal2.number =100.00; } void loop() { delay(500); } // function that executes whenever data is requested by master // this function is registered as an event, see setup() void requestEvent() { Wire.write(tal1.bytes, 4); Wire.write(tal2.bytes, 4); Wire.write(tal3); // as expected by master }

texten i den som är master

typedef union { float number; uint8_t bytes[4]; } test_t; #include <Wire.h> test_t tal1; test_t tal2; int i=0; int ii=0; int tal3=0; void setup() { Wire.begin(); // join i2c bus (address optional for master) Serial.begin(9600); // start serial for output } void loop() { Wire.requestFrom(8, 9); while (Wire.available()) { // slave may send less than requested char c = Wire.read(); // receive a byte as character // Serial.println(i); if (i<4){ tal1.bytes[i]=c; } if (i>3){ if(i<9){ ii=i-4; tal2.bytes[ii]=c; } } if (i==8){ tal3=c; } i=i+1; } i=0; Serial.println(tal1.number); Serial.println(tal2.number); Serial.println(tal3); delay(500); }

detta funkar och är någorlunda lätt att hänga med