Is er een simpele manier om twee Arduino te koppelen

@blackdog, uiteraard, een probleem tegelijk :)

Solid state is een term die ik niet eerder ben tegengekomen tijdens het programmeren. Ik ken wel stateless en zijn tegenhanger stateful. Ook blocking en nonblocking.

Zou je eens kunnen toelichten wat je bedoelt met solid state?

PE2BAS

Op het ogenblik worstel ik met de code van TI voor de LMT01.
Ik dacht dat de spanning niveaus naar de Nano niet netjes waren, maar ook met de extra transistor op de ingang die TI aangeeft te gebruiken (A1) voor mooie TTL flanken werkt de code niet.

De code die je gaf gebruikt de Analog comparator van de Mega328, Pin AIN1. Niet te verwarren met de Analog input pin A1 in arduino terminologie.

AIN1 zit op pin PD7 van de Mega328, D7 in arduino-speak.

@buckfast_beekeeper: die code buffert alleen data totdat het afsluitende karakter wordt gevonden. Dit is wat ik zelf ook zou doen, en dan pas het bericht decoderen. De code van deKees heeft minder geheugen nodig, maar wordt wel snel ingewikkelder als je meerdere verschillende velden moet kunnen ontvangen.

Het probleem is dat bij jou alle complexiteit achter "decodeJSon()" zit. Ik vermoedt dat die library vrij groot is, en vooral een forse voetafdruk in het geheugen achterlaat. Wat gebeurd er als er een misvormd pakket ontvangen wordt? Gebruikt die library dynamisch geheugen, waardoor je met een pakket met een bytefout en wat pech een heap overflow kunt krijgen?

Zoiets lijkt me een goede oplossing voor een grotere target (een Raspi of zo), maar op een target met zeer beperkt geheugen, als een Arduino, zie ik toch wel wat beren op de weg.

Een manager is iemand die denkt dat negen vrouwen in één maand een kind kunnen maken

Op maandag 6 mei 2024 13:48:31 schreef Arco:
Bij text-only is het simpelste om af te sluiten met een <cr>, ook handig voor testen met een terminal programma.

Eerlijk gezegd ben ik daar ook voorstander van.

Laatst had ik een project en ik had net de "binary interface" gemaakt en dacht: Weetjewat, als het pakket niet met STX (start transmission) begint, dan zal het wel ascii zijn, laat ik het dan de ascii verwerking inmikken. Misschien wel handig bij het debuggen.

Huidige situatie is: binary interface is nog nooit getest en klant is happy met de ascii interface....

Een binary interface, die kan iets efficienter zijn. Neem als voorbeeld DSMR (slimme meter specs). Op 9600 baud loopt dat vol: je kan niet 1x per sec een status-record sturen: Het duurt langer dan 1 sec. Tegenwoordig (DSMR 5) is de baudrate hoger en kan dat wel.

Binary had het ook op 9600 vast wel gepast.

Maar dat soort optimalisaties: "maak het lastiger om te testen, maar efficienter" moet je tegenwoordig eigenlijk liever niet meer doen. Gewoon alles leesbaar houden dat je met een suf terminalprogramma de boel gewoon kan monitoren, debuggen en testen.

Maak je een binary protocol, dan moet je zowel de zender als de ontvanger tegelijk werkend krijgen. Doe je alles in ascii, dan kan je beginnen met de slave en die stuur je met de hand commandos en wacht op de antwoorden. Dan maak je de master, en kijkt met het terminal programma of ie de juiste queries verstuurd. En pas dan hoef je de boel samen te laten werken. Meer kleine stapjes die je 1 voor 1 kan debuggen.

four NANDS do make a NOR . Kijk ook eens in onze shop: http://www.bitwizard.nl/shop/

Beide kanten is iets voor te zeggen. Een voordeel van een ASCII protocol zoals JSON is dat je velden kunt toevoegen zonder de compatibiliteit met een vorige versie te breken, zolang die nieuwe velden genegeerd kunnen worden door de oude implementatie. Natuurlijk is het niet onmogelijk om dat te doen met een binair protocol, maar daar moet je dan wel goed over nadenken.

Ik werk nogal eens met wetenschappelijk apparatuur (al dan niet van eigen ontwerp), en die praten vaak "skippy" (SCPI), een ASCII protocol waarbij je dus gemakkelijk mee kunt lezen met de communicatie.

Pas als je zoveel mogelijk data over een beperkte interface moet sturen, of voor elke verzonden byte moet betalen, heeft een binair protocol echt voordelen.

Een manager is iemand die denkt dat negen vrouwen in één maand een kind kunnen maken
blackdog

Golden Member

Hi deKees, :-)

Voor ik ging sporten vanmiddag heb ik even gezocht op de string die de comparator insteld zoals in de Code van TI staat.
Hierdoor vond ik in een forum topic dat het inderdaad digitale pin-7 moet zijn en dus niet pin A1 zoals op het plaatje van TI!

Zo kan je jezelf natuurlijk rot zoeken als een bedrijf zoals TI dat soort fouten maakt voor de documentatie van een van hun producten.
Maar goed, het is opgelost, het draait nu nog met de extra transistor er tussen, maar vanavond test ik het met alleen weer de pull down weerstand.

Dank voor de tip!

Groet,
Bram

PS
Voor alle anderen, ik begrijp natuurlijk dat er storingen in de communicatie kunnen optreden en dat kan verschillende oorzaken hebben.
Ik denk niet extern, alles zit binnen aluminium oven behuizingen met ontkoppeling en maar max. 20cm draadlengte en beperkte flanken, maar dus op het software gebied.
De Pro Mini die ik net heb binnen gekregen welke net nog wat kleiner is dan de Nano met de zelfde chip, doet alleen de PID en het serieele werk voor de communicatie naar de buitenoven en het display.
Misschien nog iets meer dat er gaat gebeuren, maar dat zijn beslissingen voor later als nodig.

Ik denk niet dat als de processor één keer hikt van de binnen oven dat het mis gaat, vooral niet bij de lage zend ontvang frequentie.
Later hier nog wat meer over, van de manier zoals ik het een en ander wil beveiligen.

You have your way. I have my way. As for the right way, the correct way, and the only way, it does not exist.

Ha! Herkenbaar. Dat noemt men "premature optimalisation"

Vaak is de minder efficiente methode prima. Als dat ook nog eens eenvoudiger te debuggen is, waarom niet?

Ook is het zo dat het financieel niet meer uitmaakt om een zwaardere processor te kopen. Mijn collega grapt wel eens, als de software te zwaar is, wacht je gewoon een jaar. Dan is er betere hardware en draait de software prima.

PE2BAS
Arco

Special Member

Neem als voorbeeld DSMR (slimme meter specs). Op 9600 baud loopt dat vol:

Da's ook niet meer van deze tijd (9600baud)... :+
Veel spul wordt tegenwoordig uitgeleverd met 230k4 als default...

Arco - "Simplicity is a prerequisite for reliability" - hard-, firm-, en software ontwikkeling: www.arcovox.com
buckfast_beekeeper

Golden Member

Op maandag 6 mei 2024 16:36:21 schreef SparkyGSX:
@buckfast_beekeeper: die code buffert alleen data totdat het afsluitende karakter wordt gevonden. Dit is wat ik zelf ook zou doen, en dan pas het bericht decoderen. De code van deKees heeft minder geheugen nodig, maar wordt wel snel ingewikkelder als je meerdere verschillende velden moet kunnen ontvangen.

Het probleem is dat bij jou alle complexiteit achter "decodeJSon()" zit. Ik vermoedt dat die library vrij groot is, en vooral een forse voetafdruk in het geheugen achterlaat. Wat gebeurd er als er een misvormd pakket ontvangen wordt? Gebruikt die library dynamisch geheugen, waardoor je met een pakket met een bytefout en wat pech een heap overflow kunt krijgen?

Zoiets lijkt me een goede oplossing voor een grotere target (een Raspi of zo), maar op een target met zeer beperkt geheugen, als een Arduino, zie ik toch wel wat beren op de weg.

Toch draait het momenteel probleemloos meer dan een jaar. een probleem bij de ATmega/arduino is de beperkte seriële buffer. Daar krijg je nooit veel gegevens in. Je kan dan kiezen om te blijven hangen in de ontvangst lus of alles wat in de buffer zit op te halen en verder te doen. Het eerste heb ik ook geprobeerd maar dat liep vaker fout dan goed. Als nu een bericht niet goed toekomt, begint de software gewoon van 0 zodra weer { wordt ontvangen. De nodes gaan ook automatisch de klep sluiten als het maximaal waterpeil wordt bereikt. Dat kan niet wachten tot alles is ontvangen of er in een lus wordt gewacht op iets wat niet meer komt.

Wat krijgt de ATmega als input?

{"Adres":255,"Jaar":2022,"Maand":12,"DagVanMaand":7,"Uur":20,"Minuten":55,"Seconden":00}

Om de RTC gelijk te zetten. IK weet dat dit niet 100% gelijk is maar meer dan voldoende voor de toepassing. 255 is het broadcast adres.

{"Adres":2, "Display": "Aan"} of {"Adres":2, "Display": "Uit"}

Adres display aan zodat ik weet dat de node als target dient. Uit zodra het is afgehandeld. Zijn gewoon 2 7 segment display voor het adres dat met 2 4543 BCD=>7 segment decoders wordt aangestuurd. Klopt het adres niet? Dan wordt al de rest genegeerd.

{"Adres":2, "Klep": "Open"}
{"Adres":2, "Klep": "Dicht"}

Water klep open of dicht.

{"Adres":2, "Status": "?"}

Hierop reageert de juiste node met

{"Adres":" 2,"Status":"Alive","Klep":"Open","Waterniveau":"OK","Temperatuur":20.5,"RTCTemperatuur":20.7,"LaatstGevuld":"Maandag 06 mei 2024 19:25:34","ErrorString":"GeenNiveau","Versie":"1.08"

Laatst gevuld zou ik ook als timestamp kunnen verzenden maar die wordt niet gebruikt voor berekeningen of zo. Alleen maar om weer te geven op de webpagina. Daar heb ik toch liefst leesbare tekst.

Solid state was de term die ik vond als ik op zoek was naar een betere manier om de seriele data te verwerken.

Denk je dat de volledig JSon library te veel is van het goede, gebruik dan de JSon decoder. De Json string maken is, zoals al gezegd, niet zo moeilijk. Dan valt alvast het encoder deel weg.


sendRs485("{\"" + Adres + "\":" + adress + ",\""
                + Status + "\":\"" + Alive + "\",\""
                + Klep + "\":\"" + klepToestand +  "\",\""
                + Waterniveau + "\":\"" + WN  +  "\",\""
                + Temperatuur + "\":" + grC + ",\""
                + RTCTemperatuur + "\":" + rtc.getTemperature() + ",\""
                + LaatstGevuld + "\":" + eetijd + ",\""
                + ErrorString + "\":\"" + strError + "\",\""
                + Versie + "\":\"" + VERSIE + "\"}");
Van Lambiek wordt goede geuze gemaakt.

Deze vraag herinnerde mij aan een projectje wat ik nog eens moest afronden. Het is bijzonder simpel. Met een commando kan je gewicht meten dmv een HX711 en een paar loadcells. Wellicht dat iemand er inspiratie uit opdoet, dus ik gooi het hier maar neer.

Je zou de boel makkelijk moeten kunnen draaien, zeker als je de HX711 dingetjes er even uit haalt.
https://github.com/vanBassum/NanoScale

Stel, je wilt met dit apparaatje praten vanuit een andere nano, dan implementeer je gewoon het commando RRAW.

[Bericht gewijzigd door hardbass op maandag 6 mei 2024 21:05:06 (13%)

PE2BAS
blackdog

Golden Member

Hi,

Ik begrijp niet goed hoe de code van TI betreffende het tellen van de pulsen uit de LMT01 sensor werkt.

Mijn aannamen:
Ik vermoed dat de werking gelijk is als de basis frequentie teller.
Binnen een bepaald tijdsbestek worden er pulsen geteld.
Maar ik kan niet zien wanneer het tellen begint en wanneer het tellen stopt in de code.

Het lijkt er op dat er gekeken wordt wanneer er een burst van pulsen binnen komt en er begonnen wordt met tellen en als de burst voorbij is het tellen stop.
De frequentie van de burst is ongeveer 88KHz en het aantal pulsen is in theorie tussen 0 en 4096, maar het lineaire werkgebied is tussen 15 pulsen en 3300 pulsen.

Dit is de burst die binnenkomt op de comparator ingang D7, als de andere comparator ingang aan de interne 1.1V referentie hangt, dit werkt gewoon, maar ik snap de code dus niet. ;)
https://www.bramcam.nl/Diversen/LMT01-Burst.png

.



volatile int pulseCount = 0;
float temperature = 0;
int hold = 0; 

void setup() 
{
  ACSR = B01011010;    //Set according to above bit description

  Serial.begin(38400);
}


void loop() 
{
  //don't bother entering the loop again if no pulses have been counted yet. 
  if(pulseCount != 0)
  {
    //Wait for counting to be complete
    while(pulseCount != hold)
    {
      hold = pulseCount;
      delay(1);
    }
  
 
    
    //Print Temperature
    temperature = 0.0625 * pulseCount - 50;
    Serial.println(temperature);
  
    
    //reset pulseCount for next loop
    pulseCount = 0;
  }
  delay(2);
}


//Interrupt Service Routine, counts pulses
ISR(ANALOG_COMP_vect) 
{
  //Increment pulse count
  pulseCount += 1;
 
}

Dank en groet,
Bram

You have your way. I have my way. As for the right way, the correct way, and the only way, it does not exist.

Dat lijkt toch een vrij eenvoudig systeem, als je snapt hoe die interrupt werkt.

in ISR(ANALOG_COMP_vect) wordt de pulscount verhoogd bij elke interrupt, dat zal elke opgaande of neergaande flank zijn, denk ik.

In de loop staat

    //Wait for counting to be complete
    while(pulseCount != hold)
    {
      hold = pulseCount;
      delay(1);
    }

Daar wordt dus de huidige waarde (pulseCount) gekopieerd naar de variabele hold, en 1ms later wordt de pulseCount nog een keer gelezen. Als die gelijk is aan hold, is er in de laatste milliseconde dus geen flank geweest, dus is de pulstrein voorbij. Als ze niet gelijk zijn, is de pulstrein blijkbaar nodig bezig, en gaat hij weer 1ms slapen om daarna nog een keer te kijken.

Dit heeft dus wel als gevolg dat het programma daar voor onbepaalde tijd blijft hangen als er pulsjes blijven komen; bijvoorbeeld omdat die pin los hangt en rommel uit de omgeving opvangt. Een trein van 4096 pulsen op 88kHz zou hooguit 47ms kunnen duren, dus het zou handig zijn om bij te houden hoe vaak die loop doorlopen is, en deze te stoppen als het meer dan 47x is (met een beetje marge voor een afwijking in die 88kHz en je eigen klok).

Daarbij krijg je een verkeerde meting als je begint terwijl de pulstrein al bezig is; in dit geval zou dat alleen kunnen gebeuren bij de eerste meting na het opstarten, maar is wel iets om over na te denken, als het ook in andere situaties zou kunnen gebeuren, en je geen manier hebt om dat te detecteren.

[Bericht gewijzigd door SparkyGSX op dinsdag 7 mei 2024 09:55:32 (41%)

Een manager is iemand die denkt dat negen vrouwen in één maand een kind kunnen maken
buckfast_beekeeper

Golden Member

ISR(ANALOG_COMP_vect) 
{
  //Increment pulse count
  pulseCount += 1;
 
}

Als de comparator een ingesteld punt overschrijdt wordt er een interrupt gegenereerd. Die interrupt is bovenstaande code. Daarom is pulseCount ook als volatile gedeclareerd. Verder

 if(pulseCount != 0)
  {
    //Wait for counting to be complete
    while(pulseCount != hold)
    {
      hold = pulseCount;
      delay(1);
    }

Zodra pulseCount verschillend is van 0 (kon net zo goed geschreven zijn als pulseCount>0) wordt er in while(pulseCount != hold) gekeken of er nog pulsen geteld werden. Is pulseCount = hold is het aantal pulsen geteld.
delay(1) vertraagt de loop tijdens het tellen met 1ms. Anders zou de loop sneller kunnen zijn dan er pulsen binnen komen.

Dus samengevat de telling start na de eerste interrupt en stopt zodra pulseCount en hold gelijk zijn.

Van Lambiek wordt goede geuze gemaakt.

Er wordt eea geinitializeerd zodat "pulsecount" het aantal pulsjes telt.

De LOOP die wacht totdat er geteld wordt. Als er eenmaal geteld wordt (i.e. er is een burst gaande), dan wacht ie 1ms... In die tijd zou op 88khz er zo'n 88 pulsjes gezien moeten zijn, dus zolang de puls nog loopt, blijft 1ms later de pulscount != hold.

Maar zodra 1ms later de waardes gelijk zijn, dan zal de burst wel voorbij zijn. Dan rekent hij het resultaat uit en drukt dat af. Door pulscount op nul te zetten kan ie de volgende keer zien dat het tellen weer begonnen is.

Ik zie dat anderen sneller typen.

Edit: @buckfast_beekeeper : Even uitleggen wat volatile doet:

   while(pulseCount != hold)
    {
      hold = pulseCount;
      delay(1);
    }

Als er geen volatile bijstaat, dan ziet de compiler: Hey de pulsecount verandert niet binnen de loop, dus die hoef ik niet binnen de loop op te halen en in een register te zetten, maar dat kan vooraf. Dat scheelt een paar instructies binnen de loop.

[Bericht gewijzigd door rew op dinsdag 7 mei 2024 10:15:03 (26%)

four NANDS do make a NOR . Kijk ook eens in onze shop: http://www.bitwizard.nl/shop/

Op maandag 6 mei 2024 18:05:27 schreef SparkyGSX:
Pas als je zoveel mogelijk data over een beperkte interface moet sturen, of voor elke verzonden byte moet betalen, heeft een binair protocol echt voordelen.

Dan kun je altijd nog overstappen op zippen (comprimeren) van je ascii-data. Grote kans dat dat nog minder data oplevert dan een direct binair protocol

Dan moet het wel zo zijn dat er helemaal geen realtime aspecten aan de data zitten. Wat als we GPS updates willen doorsturen en de locatie verandert niet.

52.0000N 4.0000E
52.0000N 4.0000E
52.0000N 4.0000E
52.0000N 4.0000E
52.0000N 4.0000E
52.0000N 4.0000E
52.0000N 4.0000E
52.0000N 4.0000E
52.0000N 4.0000E

De compressie ziet: Ohhh, dit kan ik vast als er NOG een keer 52.0000N 4.0000E komt NOG efficienter coderen, dus daar komt vooralsnog helemaal niets uit.... Als die updates iedere 10 sec gestuurd worden, heb je ondertussen al anderhalve minuut geen idee of de boel uitgevallen is of stationair is. (of dat de compressie een andere truuk heeft gevonden om het efficient te coderen en daarom nog niets stuurt).

Een binair, geoptimaliseerd protocol is beter: Je codeert de "delta" t.o.v. het vorige punt en codeert dat bijvoorbeel met een efficiente binary code doorstuurt.

Maar goed. begint wel erg offtopic te gaan.

Ascii is tegenwoordig te preferen zodat je met een terminal programma zowel de slave als de master kan nadoen om eea te testen. Dit omdat je zelden een "bandbreedte probleem" hebt omdat alles makkelijk zoveel sneller kan dan vroeger.

four NANDS do make a NOR . Kijk ook eens in onze shop: http://www.bitwizard.nl/shop/

Dat ligt eraan; de meeste compressie algoritmes worden efficiënter naarmate je meer data comprimeert, omdat ze een vaste overhead hebben. Daarbij ligt het aan de entropie van de data, tekst is goed te comprimeren (omdat het om te beginnen inefficiënt is in het vastleggen van data), terwijl data die bijna random lijkt juist heel slecht te comprimeren is.

Ik heb aan een project gewerkt voor het verzamelen, decoderen en verzenden van data uit opstellingen voor deeltjesfysica, waar tientallen gigabits per seconde verstuurd werd (per server), en zolang de data saai was, zou je dat goed kunnen comprimeren, maar als er spannende dingen gebeuren, neemt de entropie van de data enorm toe, en dat is precies de data die je echt niet weg wilt gooien.

Daarbij zou je dan zo'n enorme hoeveelheid data moeten analyseren en comprimeren, terwijl het al moeilijk genoeg was om die data onveranderd snel genoeg door de pompen naar de serverfarm.

Voor microcontrollers is het ook niet echt een optie, aangezien je beperkt bent in de processorcapaciteit en geheugen. Als het over een heel beperkte datalink moet (zoals Lora) zou het kunnen, maar dan nog denk ik dat een goed ontworpen binair protocol efficiënter zal zijn dan gecomprimeerde tekst. Als je het, om te debuggen, toch al weer moet uitpakken, kun je m.i. net zo goed een Python scriptje schrijven om het binaire protocol te decoderen.

Een manager is iemand die denkt dat negen vrouwen in één maand een kind kunnen maken
blackdog

Golden Member

Hi heren,

Dank voor het uitleggen, ik begrijp nu de manier waarop het werkt.

SparkyGSX, ik begrijp dat het op bepaalde manieren fout kan gaan.
Dat was ook de rede dat ik de vraag stelde, want er gebeurd nog meer in de loop zoals de PID en ik heb noet niet uitgezocht of de PID software ook interups gebruikt.

deKees heeft de communicatie als van interupt pinnen afgehaald en die communicatie is voor terug melding, maar verder niet belangrijk voor het gedrag van de oven, alleen de uitlezing op een display van het apparaat waar het geheel inkomt.

Externe storingen en de datapin van de LMT01 denk ik niet dat dit voor gaat komen, wat ik al zij,
de bedrading is kort en de MOSFet die de vier verwarmingsweerstanden aanstuurd heeft in verhouding trage flanken gekregen door mijn schakeling opbouw.
De PID is in het geheel traag door de thermische massa van de binnen oven, dus ook al zou er een error optreden dan zou dit geen grote temperatuur sprongen opleveren vande oven.

Omdat ik eerst vorige week heb getest met andere LMT01 software was ik al van plan de pulstrein te vergelijken met ene gemidelde waarde.
Dus als de oven op 50C is gebracht dan is de pulswaarde rond de 1600 en als de volgende waarde dan 1750 is of 48, dan kan je deze waarde negeren en
de volgende waarde afwachten die binnen de bandbreedte valt van de gekozen limiten.

Plaatjes tijd
Dit zijn twee kleine oventjes die voor nu even tegelijk in de buitenoven behuzing liggen, naturulijk komt maar één van de oventjes in de buitenoven.
De kleinste oven links heeft aleen aan de onderzijde twee BPR10 weerstanden van 100 Ohm die parallel staan.
De rechter waar ik nu aan het voorbereiden ben heeft vier TO220 weerstanden, twee op de bodem en twee aan de zijkanten, dit om de energie zo goed mogelijk te verspreiden.
De rand van het kast moet ik nog wat meer vlak maken voor betere overdracht naar de deksel.
https://www.bramcam.nl/Diversen/Dual-Alu-Oven-01.png

.
Dit is de binenzijde van het oventje, in het midden de Pri Mini gelijmd op twee Alu oxide thermische plaatjes, dit voor de electische isolatie, dit vond ik een nette manier om het zo te doen.
De Pro Mini heeft namelijk geen bevestiging gaatjes.
Op een van de verwarmings weerstanden is een platic IRF540N gemonteerd, de dissipatie door de PWM is laag.
De electronica die verwarmt moet worden, wordt gemonteerd op de twee nylon afstandbusjes.
https://www.bramcam.nl/Diversen/Dual-Alu-Oven-07.png

.
Op dit plaatje is het een en ander wat beter zichtbaar.
Er is nog een kleine kans dat de IRF540 toch naar de bodem gaat, dit is afhankelijk van de testen die ik vanavond wil gaan doen met de MOSFet en de vier 10 Ohm weerstanden.
Om de stoorsignalen klein te houden ga ik de flankstijlheden beperken, dat ka nflink trager daar de PWM frequentie niet boven de 10Hz zal uitkomen en uit ervaring met een andere PID oven weet ik dat rond 2Hz ik een hele mooie regeling kreeg.
Dus er zijn wat metingen nodig rond de 2 tot 5 Hz met de pulsbreedte die het equilibrium goed kunnen bereiken.
Als ik de flanken traag maak, dan is dat goed voor het stoorniveau maar niet goed voor de regel eigenschappen.
Voor de testen breng ik de temperatuur maar 5C boven de omgevings temepratuur om te zien hoeveel energie er nodig is de binnenoven op deze temperatuur te houden.
Dan kan ik ook zien van de verwachte pulsbreedte is bij de gebrukte voeding spanning bij de 5C hogen dan de buiten oven.
https://www.bramcam.nl/Diversen/Dual-Alu-Oven-08.png

.
Hier is wat beter te zien hoe de Pro Mini op de isolatie plaatjes is gelijmd.
https://www.bramcam.nl/Diversen/Dual-Alu-Oven-09.png

.
Laters meer, zoals het testen van wat code tegelijk draaiend voor de binnen oven en of ik dan met de deKees code error voorbij zie vliegen. :-)

Groet,
Bram

You have your way. I have my way. As for the right way, the correct way, and the only way, it does not exist.

Voor communicatie gebruiken we nu de SoftwareSerial library. Die gebruikt bit-banging om een serial port na te bootsen in software, zoals de naam al zegt.

Ik heb de source code er bij gehaald, en dan blijkt het volgende:

  • Ontvangen van een byte begint met een Pin change interrupt.
  • De interrupt handler komt niet meer terug voordat het hele byte binnen is.
  • Ontvangen bytes worden weggeschreven in een buffer waar ze kunnen worden opgehaald.
  • Transmit gebeurt in een blocking aanroep.
  • Interrupts worden gedisabled gedurende de transmit van een complete byte.

Op zich is dit een goede strategie. Daarmee kun je behoorlijk goed communiceren.

Maar dat kan wel storen met het inlezen van de temp sensor. Die sensor bepaalt zelf wanneer een nieuwe pulstrein begint, en als er op dat moment gecommuniceerd wordt dan ga je pulsen missen. En ook als er serial data binnenkomt gedurende een puls-trein dan kun je pulsen missen.

Dat kun je op meerdere manieren oplossen:

  • Direct na communicatie de puls-trein her-initialiseren (en wachten tot de lijn stabiel is)
  • Of toch gebruikmaken van de hardware-uart voor de processor bij de sensor.
blackdog

Golden Member

Hi deKees,

Dank weer voor de info.

Kan ik de variabele van de tempsensor niet gebruiken om data heen en weer te sturen?

Deze in de binnen oven:


    //reset pulseCount for next loop
    pulseCount = 0;

Als pulseCount "0" is kan de seriele bus even data sturen en daarna weer zwijgzaam zijn tot hij de volgende keer wordt aangeroepen.
Dat hoeft dus niet iedere loop ronde, maar b.v. 1 a 2 keer per seconde is mooi zat.
Ik doe de aanname dat de seriele bus software makkelijk wat tijd betreft tussen de sersor burst door, even zijn data kan ontvangen/verzenden.

Waar beheer ik jouw seriele bus software in de code, om dit mogelijk te maken?

Groet,
Bram

You have your way. I have my way. As for the right way, the correct way, and the only way, it does not exist.
benleentje

Golden Member

Waar beheer ik jouw seriele bus software in de code, om dit mogelijk te maken?

De software in een microcontroller is een eindeloze lus. Bestaat de lus uit weing regels dan word dat bv 10.000x per seconde doorlopen.

Nu zal de met softserial een stuk trager gaan en zeg 500x per seconde. Softserial zelf is zo gemaakt dat zodra je die regel aanroept met bv

myserial.print ("Blackdog") 

dat dat meteen word uitgevoerd.

Als je dan wilt dat die regel maar om de seconde word uitgevoerd dan zal dat zelf moeten programmeren.
1) manier is via millis() en je kijkt dan of er 1000 miliseconde is verstreken door bv

If Millis() + 1000 > Vorigewaarde then myserial.print bla bla

2) of je stuurt enkel een nieuwe temperatuur als die is verandert

Zowel de SoftwareSerial als de Temp sensor zijn tijd kritisch. En dat maakt het lastig om die samen in 1 controller te zetten.

Met een hogere baudrate wordt de communicatietijd korter. En dan heeft de temp sensor er minder last van. 57k6 blijkt de max te zijn. Daarboven krijg je communicatie fouten.

En ook het versturen van data naar de temp sensor processor verstoort de temp meting. Dat kun je dus beter weglaten.

Ook het bericht heb ik wat aangepast:
- Geen STX/ETX meer. alles in leesbaar ASCII
- Bericht begint met een letter A-Z
- Dan een getal (minteken, cijfers, punt)
- En afsluiten met CR

En na een transmit wordt een eventuele nieuwe -incomplete- pulstrein gedetecteerd en weggewerkt

Dan krijg je een programma als dit om de temp te lezen.



#include <SoftwareSerial.h>


// Set up a new SoftwareSerial object
enum
{  // Pin Numbers 
   RxPin = 8,
   TxPin = 9,
};

SoftwareSerial mySerial =  SoftwareSerial(RxPin, TxPin);

// =================================================================================================

// Xmit function. 
// - All printable ASCII.
// - Terminated by <CR>
void XmitMessage(char MsgType, float Value)
{  mySerial.write(MsgType);    // [A - Z] To indicate type of value value 
   mySerial.print(Value, 2);   // Numeric value in ASCII, float with 2 decimals
   mySerial.write('\r');       // To indicate end-of-message
}

// =================================================================================================

void setup() 
{  // Debug port to PC over USB
   // - Hardware uart
   Serial.begin(38400);

   // Comms channel to other Arduino
   // - Software uart
   // - Max baudrate 
   pinMode(RxPin, INPUT);
   pinMode(TxPin, OUTPUT);
   mySerial.begin(57600);


   //Setup ACSR Register, to initlize the comparator
   /*         ACSR Bit Description
   ACD  - Clear ACD to enable Analog Comparator
   ACBG - Set ACBG to 1 to use internal 1.1V Reference
   ACO  - Clear ACIO (Will be ignored - read only)
   ACI  - Reset Analog Interrupt Flag by writing 1
   ACIE - Set ACIE to enable comparator interrupt
   ACIC - Clear ACIC, no connection to Timer/counter
   ACIS1 - Set ACIS1 to trigger interrupt on falling edge
   ACIS0 - Cleat ACIS0 to trigger interrupt on falling edge
   */
   ACSR = B01011010;    //Set according to above bit description

   Serial.println("Hello World");
}

// =================================================================================================

volatile int pulseCount = 0;
         int hold = 0; 


//Interrupt Service Routine, counts pulses
ISR(ANALOG_COMP_vect) 
{
   //Increment pulse count
   pulseCount += 1;
}

// =================================================================================================

float Accumulator = 0.0;
float PulseTotal  = 0.0;
int   NrSamples   = 0;
         
float temperature = 0.0;


void loop() 
{
   // Xmit once a second
   static uint32_t Timer = millis();

   const  uint32_t Interval = 1000;

   // don't bother entering the loop again if no pulses have been counted yet. 
   if(pulseCount != 0)
   {  
      // Wait for counting to be complete
      while(pulseCount != hold)
      {  hold = pulseCount;
         delay(1);
      }
      
      // Setup for next pulse-train
      pulseCount = 0;

      // Calc temp from hold value
      // - As pulseCount may be changed by new pulse-train
      temperature = 0.0625 * hold - 50;

      // Calc sum of all samples so we can calc Average
      Accumulator += temperature; 
      PulseTotal  += hold; 
      NrSamples   += 1;
   }

   // Only transmit on Interval time.
   if( (millis() - Timer) > Interval )
   {  Timer += Interval;

      if(NrSamples > 0)
      {  // Send actual average temperature
         float AverageTemp  = Accumulator / NrSamples;
         float AverageCount = PulseTotal  / NrSamples;
         XmitMessage('T', AverageTemp  );
         XmitMessage('C', AverageCount );
         XmitMessage('S', NrSamples    );
  
         Accumulator = 0.0; 
         PulseTotal  = 0.0; 
         NrSamples   = 0;
      }
      else
      {  // Send error. No pulsetrain from sensor.
         XmitMessage('E', 0.0 );
      }

      // Reset pulseCount for next loop
      // - Discard possible new -incomplete- pulsetrain
      while(pulseCount != 0)
      {  pulseCount = 0;
         delay(1);
      }
   }
}

En dit om het uit te lezen:



#include <SoftwareSerial.h>

// Set up a new SoftwareSerial object
enum
{  // Pin Numbers 
   RxPin = 8,
   TxPin = 9,
};

static const uint32_t Interval = 1000;

SoftwareSerial mySerial =  SoftwareSerial(RxPin, TxPin);

// =================================================================================================

class MSG_HANDLER
{
public:  
   uint8_t MsgState = 0;
   char    MsgType  = '-';

   float Value      = 0.0;
   float Exponent   = 1.0;
   bool  HasDecimal = false;

   void Setup()
   {  Value      = 0.0;
      Exponent   = 1.0;
      HasDecimal = false;
   }
   
   char HandleChar(char c)
   { 
     char Result = 0x00;

     if ( (c >= 'A') && (c <= 'Z') )
     {  Setup();
        MsgType  = c;
        MsgState = 1;
     }
     else if(MsgState == 1)
     {
       if(c == '\r')
       {  Value /= Exponent;
          
          MsgState = 0;
          Result = MsgType;
       }
       else if(c == '-')
       {  Exponent = -1;
       }
       else if(c == '.')
       {  HasDecimal = true;
       }
       else if(isDigit(c))
       {  Value = Value * 10. + (c - '0');
          if(HasDecimal)
          {  Exponent *= 10.;
          }
       }
       else
       {  MsgState = 0;  // Invalid char received.
       }
     }
     return Result;
   }
};

MSG_HANDLER MyHandler;

// =================================================================================================

void setup() 
{  // Debug port to PC over USB
   // - Hardware uart
   Serial.begin(38400);

   // Comms channel to other Arduino
   // - Software uart
   pinMode(RxPin, INPUT);
   pinMode(TxPin, OUTPUT);
   mySerial.begin(57600);

   Serial.println("Hello World");
}

// =================================================================================================

void loop() 
{  
   // Always receive
   mySerial.listen();
   if(mySerial.available())
   { 
      char c = mySerial.read(); // Field Type.
    
      switch( MyHandler.HandleChar(c) )
      {  case 'T':
         {  Serial.print  ( F("Avg Temperature : ") );
            Serial.println( MyHandler.Value );
            break;
         }
         case 'C':
         {  Serial.print  ( F("Avg Nr Pulses   : ") );
            Serial.println( MyHandler.Value );
            break;
         }
         case 'S':
         {  Serial.print  ( F("Nr Samples      : ") );
            Serial.println( MyHandler.Value );
            break;
         }
         case 'E':
         {  Serial.println( F("No pulses from sensor") );
            break;
         }
         case 0x00:
         {  // No data / Msg incomplete / Error
            break;
         }
         default:
         {  Serial.print  ( F("Unknown field type : ") );
            Serial.println( MyHandler.MsgType );
            break;
         }
      }
   }
}

PS, ik kan hier niet testen bij gebrek aan zo een temp-sensor.

blackdog

Golden Member

Hi deKees, :-)

Ik ben je niet vergeten en ook de code niet die je geschreven hebt.
Ik zit met een kleine verbouwing in mijn keuken en dat gaat even voor.

Maar komende zondag of maandag ga ik je code even testen met de LMT01 er bij.

Groet,
Bram

You have your way. I have my way. As for the right way, the correct way, and the only way, it does not exist.
blackdog

Golden Member

Hi,

Net een klein beetje tijd vrigemaakt om de code van deKees te testen, dit is nog zonder de PID software.
Dit omdat ik daarvoor nog wat moet solderen aan het oventje.

Dit is het onderstaande resultaat van het zend en ontvangdeel.
https://www.bramcam.nl/Diversen/Dual-Alu-Oven-10.png

De pulsen komen binnen en worden gemiddeld weergegeven. dit net als de temperatuur.

Vanavond werk ik wat aan de bedrading van de binnen oven weerstanden en het lijmen van de LMT01 aan de behuizing.

Groet,
Bram

You have your way. I have my way. As for the right way, the correct way, and the only way, it does not exist.
benleentje

Golden Member

Voor mij is Nr pulses dubbel op. Pulses is al in het meervoud hetzelfde met samples.

Voor de restgoed bezig ;).