Is er een simpele manier om twee Arduino te koppelen

Op zondag 5 mei 2024 00:58:34 schreef henri62:
Begin alsjeblief niet met I2C te rommelen, is niet bedoeld voor lange afstanden en is een stuk lastiger te programmeren.

Valt best mee, hoor... :)

We hebben in tientallen grote installaties door Europa voor lange tijd i2c netwerkjes van tientallen meters gebruikt met tot 10 units aan de bus zonder ooit een probleem.
Zaten wel drivers tussen (P82B715 extender/driver)

Programmeren is probleemloos (zolang je je aan de i2c regels houdt...)

Waarom zou je dat doen, in plaats van een veldbus (RS422/485, of CAN bijvoorbeeld), die daarvoor bedoeld zijn, die zijn differentieel en hebben een veel hogere common mode rejection. De enige reden die ik kan bedenken is dat je devices hebt zover microcontroller, dus alleen I2C slaves.

I2c is bedoeld als bus in een device en bij voorkeur om op dezelfde PCB te blijven. Niet voor afstanden.

Rs232 zou boven 10 meter bij mij ook afvallen.

Can of rs485 of rs422 kan prima, maar 422 is wellicht iets overkill, daar volledig full duplex hier niet nodig zal zijn.

Morge,

Ik ben nog steeds aan het afwegen op welek manier ik het ga doen.
Omdat jullie ook steeds de serieele communicatie aangeven via opto heb ik daar vanmorgen op gezocht of ik wat handigs kon vinden dat voor mij makkelijk toepasbaar is.
Niet vergeten, taal en programmeren is erg lastig i.v.m. dyslectie. ;)

Voor serieele communicatie kijk ik nu naar deze video, deze man heeft een library gemaakt zodat de communicatie voor mij begrijpelijk te programmeren is.
Wat betreft de optos en snelheden enz dat is geen probleem, dat is analoge electronica, gaat goed komen. ;)

https://www.youtube.com/watch?v=V2CsT7DKX4E

PS, Ik kom er net achter door in de library te kijken die i nde video gebruikt wordt, dat deze gewoon i2c gebruikt. :-)

Even over de afstanden binnen dit project, denk dan aan bedrading van "Keynar draad" van niet langer dan 20cm en alles getwist ter voorkoming van stoorvelden.

Voorlopig wat ik denk nodig te hebben
Vooral de temperatuur van de Arduino in de binnen oven overbrengen naar de Arduino van de buiten oven.
Ik wil in principe de binnen oven als een zelfstandige unit uitvoeren, en alleen wat sensor data (temperatuur) naar de hoofd Arduino transporteren, dat zou voor nu voldoende zijn.
Maar ik zou het leuk vinden als er communicatie symetrisch kan plaats vinden tussen de twee Arduino's voor extra functionaliteit.
Behalve een goed werkend product, wil ik er ook wat van leren software matig, dit zonder dat het software deel dominant wordt.

Ik ga een testsetup maken met twee computers en dan de twee Arduino's voorlopig gewoon hard bedraden, dat is dan voldoende om de software te testen.

Groet,
Bram

Er zijn hier genoeg mensen (waaronder ondergetekende) die je wel willen helpen met de code, op een didactisch verantwoorde manier ;)

Voor een serieel protocol moet je een paar keuzes maken; de eerste lijkt me of je een human-readable of binair protocol wilt gebruiken. Binair is doorgaans eenvoudiger te implementeren, human-readable is eenvoudiger te begrijpen en te debuggen. Het lastige van human-readable is dat de ontvanger het weer om moet zetten naar binaire gegevens waar je mee kunt rekenen, maar dat hoeft niet dramatisch moeilijk te zijn.

Zijn ESP modules (te programmeren met de Arduino IDE) geen optie ?
Dan kun je data uitwisselen via WiFi.

Zo een nano heeft een enkele uart ingebouwd en die wil je waarschijnlijk vrij houden om te debuggen. Toch in elk geval op de buitenprocessor. Dan heeft Arduino nog wel een optie voor een 2de software uart. Die is ook goed te gebruiken.

Een seriele poort verstuurt bytes. Om daar zinvolle data uit te halen moet je een protocol afspreken tussen zender en receiver. Dwz dat je een stel regels moet afspreken over de opbouw van de berichten.

Bijvoorbeeld:

  • Een bericht begint met STX (0x02)
  • Het eerste byte geeft een data type.
    Bijv 'T' voor temperatuur
  • Daarna komt de waarde in ASCII
    - Alleen cijfers en evt een decimale punt
  • En het bericht wordt afgesloten door ETX (0x03).

Hierbij een voorbeeldje van een transmitter



#include <SoftwareSerial.h>


// Set up a new SoftwareSerial object
enum
{  // Pin Numbers
   RxPin    = 2,
   TxPin    = 3,

   Interval = 1000,
};

// ASCII Protocol chars
static const byte STX = 2;
static const byte ETX = 3;


SoftwareSerial mySerial =  SoftwareSerial(RxPin, TxPin);

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(9600);

   Serial.println( F("Hello World") );
}



void loop() 
{  
   static uint32_t Timer = millis();

   if((millis() - Timer) > Interval)
   {  Timer += Interval;
   
      float temp = 12.7;
   
      mySerial.write(STX);      // Start of message
      mySerial.write('T');      // To indicate temperature value 
      mySerial.print(temp, 2);  // Xmit in ASCII, float with 2 decimals
      mySerial.write(ETX);      // End of message
      
      Serial.println(".");
   }
}

En een receiver



#include <SoftwareSerial.h>


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

// ASCII Protocol chars
static const byte STX = 2;
static const byte ETX = 3;


SoftwareSerial mySerial =  SoftwareSerial(RxPin, TxPin);


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(9600);

   Serial.println("Hello World");
}


// Read a float from mySerial.
// - Float is received in ASCII and terminated by ETX 
float ReadFloat()
{
   float Value     = 0.0;
   float Exponent  = 1.0;
   bool HasDecimal = false;

   for( ;; )
   {  char c = mySerial.read();
      
      if(c == -1)
      {  return NAN;   // No data received
      }
      else if(c == ETX)
      {  return Value / Exponent;
      }
      else if(c == '.')
      {  HasDecimal = true;
      }
      else if(isDigit(c))
      {  Value = Value * 10. + (c - '0');
         if(HasDecimal)
         {  Exponent *= 10.;
         }
      }
   }
}


void loop() 
{  delay (1000);

   mySerial.listen();
   
   if(mySerial.available())
   { Serial.println( F("We have Data : ") );

     if(mySerial.read() == STX)
     {  char c = mySerial.read(); // Field Type.
        if(c == 'T')
        {  float Temp = ReadFloat();
           if(Temp == NAN)
           {  Serial.println( F("Communication error") );
           }
           else
           {  Serial.println( String(F("Temperature : ")) + Temp );
           }
        }
        else
        {  Serial.print( F("Invalid field type\n") );
        }     
     }
     else
     {  Serial.print( F("Invalid Data\n") );
     }
   }
   else
   {  Serial.print( F("No Data\n") );
   }
}

Software uart is geconfigureerd op Pin 2 (Receive) en Pin 3 (Transmit). Dus die moeten kruislings gekoppeld worden tussen transmitter nano en receiver nano.

Persoonlijk zou ik ervoor kiezen om alleen integers over te sturen, en dan de temperatuur *100 of *1000 te sturen, bij voorkeur met een vaste lengte, met voorloopnullen, dat maakt de ontvangende kant simpel.

Hi Heren,

Net terug van 2,5 uur lopen, ben gesloopt. :+

deKees, hartelijk dank.
Jouw serieele code lijk wat anders dan het gene dat ik al heb gevonden.
Aan de ontvangst kant lijkt wat code te zitten dat een controle uitvoerd, heb ik dat goed?

Nog wat info
Het belangrijkste wat overgezet gaat worden is een getal tussen 1100 en 1700 en daar wordt dan de temperatuur uit berekend.
De LMT01 levert een getal tussen 15 en 3231 afhankelijk van de tempratuur welke dan via een simpel formule wordt omgezet naar de Celcius temperatuur.
De binnen oven zal rond de 50C worden gebracht door de PID van de binnen oven voor 50C levert de LMT01, 1603 pulsen.
Die 1603 waarde wordt dan ook het setpoint van de binnen oven PID.

Dan weer even terug naar de code van deKees.
Ik neem aan dat ik ook andere pinnummer kan nemen dan 2 en 3 voor de SoftwareSerial, dit omdat ik pin-2 voor de LMT01 nodig heb, deze heeft de interupt nodig of de comperator ingang.
Dit afhankelijk van welke software ik voor de LMT01 ga gebruiken.

Als ik vanavond weer een beetje uitgerust ben, laat ik je software even in twee Nano's die ik al klaar heb liggen op mijn tweede werkbank met twee computers.

Dank en groet,
Bram

PS
Ik dacht laat ik de laatste versie van de Arduino software onde Windows eens gaan gebruiken.
Daar zit een enorm storende BUG in, alle files die je maakt maakt deze software Read Only.
Ook als je de DIR waar je ze wilt plaatsen op een andere plek zet, b.v. C:\Arduino-Code, wat een dumbo's...

Dus ik draai nu op de twee computers die ik ga gebruiken weer met 1.8.13 versie van de IDE

Maar waarom wil je een Arduino Nano in de oven, in plaats van die pulstrein van de sensor door een optocoupler te halen? Ik denk dat je die sensor met een weerstand in serie kunt zetten met de LED, eventueel met een weerstandje parallel aan de LED zodat hij in rust de hele voedingsspanning krijgt.

Hi SparkyGSX,

Het doel is een oventje te maken waarbij alles in het oventje bevind, dus de PID controler, power weerstanden en de MOSFet die de weerstanden schakeld.

Dat het er buiten ook kan dat weet ik, maar er zijn nogal wat afwegingen die ik moet doen om het zo goed mogelijk te doen wat de oventjes betreft.
Dit weer samen dat ik er wat van wil leren. :-)

De LMT01 is een twee draads digitale sensor die zijn waarde als een stroombron naar de buitenwereld brengt.
Met een goed gekozen weerstand kan hij aan een digitale ingang geknoopt worden, of zoals TI het doet voor de Arduino aan de comparator ingang.
Heb je geen comparator ingang tot je beschikking, dan kan je ook een simpele NPN gebruiken met twee weerstanden om hem aan een digitale ingang te hangen.
De LMT01 is een hele kleine plastic TO92 sensor met twee pootjes, makkelijk goed te lijmen op eengoed gekozen meetpunt.

De Code van deKees draait hier nu op twee Arduino's en ik heb al wat zitten spelen met wat waarden in de code.

Als ik de "waarde" T in de onderstaande code aanpas, dan komt de inhoud van temp niet meer aan bij de ontvanger,
of beter die weet dat hij "T" als label moet ontvangen en niet label "Q" die b.v. een 0 of een 1 bevat uit de schakeling die op temperatuur gehouden wordt.
Als ik beide zijden T door Q vervang dan komt de waardie in de niet veranderde "temp" variabel gewoon weer binnen

 mySerial.write('T');      // To indicate temperature value 

Ik neem dus het onderstaande aan en jullie mogen zeggen of ik het goed begrepen heb.

Aan de zend kant bevat de "temp" variabele de data die moet worden overgezonden naar de ontvanger.
"T" is een Label dat ook mee gezonden wordt om aan te geven dat de inhoud b.v. de temperatuur waarde betreft.

Ik kan in het oventje ook een luchdruk sensor opnemen dan wordt "T" b.v. "L" en de variabel "temp" krijgt dan de waarde uit de luchdruk sensor.
"temp" zou natuurlijk een aandere naam moeten krijgen als ik meerdere status signalen zou willen gaan overzetten van de binnen oven naar de buitenoven.
Voor de duidelijkheid, de positie "T" moet aan beide zijde gelijk zijn om de goede data te ontvangen.

Als het allemaal klopt en het is goed ingedaald bij mij, dan zou ik ook nog alleen de data willen zien als deze correct is.
Hier onder twee plaatjes van als het goed gaat met de overdracht, en als er iets mis gaat.
Sorry voor de minder goede plaatjes, ik heb op mijn tweede meetbank nog niet alles helemal ingericht, dus de plaatjes komen van de telefoon.

Hier komt goede data binnen op de ontvangkant.
https://www.bramcam.nl/Diversen/Dual-Alu-Oven-06.png

.
En dit is een stukje dat aangeeft dat er geen data binnen komt, data die niet correct was en Invalid Data.
https://www.bramcam.nl/Diversen/Dual-Alu-Oven-05.png

.
Dit is de test opstelling, links de zender en rechts de ontvangende Nano.
https://www.bramcam.nl/Diversen/Dual-Alu-Oven-04.png

Nu nog steeds te gaar van het sporten, morgen er weer even fris tegenaan, dit natuurlijk, als ik omhoog kan komen uit bed. *grin*

Dank en groet,
BRam

Toch wat te snel geweest.
De receiver werkt alleen door de delay() bovenin de loop().
Daardoor is -meestal- het hele bericht al binnen voordat het programma begint met de interpretatie van de data.

Maar normaal moet je er altijd rekening mee houden dat het bericht nog niet compleet is. En dan is een state machine al snel nodig.

Hierbij een nieuwe versie van de receiver:



#include <SoftwareSerial.h>


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

// ASCII Protocol chars
static const byte STX = 2;
static const byte ETX = 3;

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;
   }
   
   bool HandleChar(char c)
   {
     if(c == STX)
     {  Setup();
        MsgState = 1;
        return false;
     }
     else if(MsgState == 1)
     {  MsgType  = c;
        MsgState = 2;
        return false;
     }
     else if(MsgState == 2)
     {
       if(c == ETX)
       {  Value /= Exponent;
          return true;  // true when message complete
       }
       else if(c == '.')
       {  HasDecimal = true;
          return false;
       }
       else if(isDigit(c))
       {  Value = Value * 10. + (c - '0');
          if(HasDecimal)
          {  Exponent *= 10.;
          }
          return false;
       }
       else
       {  MsgState = 0;  // Invalid char received.
          return false;
       }
     }
     else
     {  MsgState = 0;
        return false;
     }
   }
};

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(9600);

   Serial.println("Hello World");
}

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

void loop() 
{  
   mySerial.listen();

   if(mySerial.available())
   { 
      char c = mySerial.read();

      if(MyHandler.HandleChar(c))
      {  
         switch (MyHandler.MsgType)
         {  case 'T':
            {  Serial.println( String(F("Temperature : ")) + MyHandler.Value );
               break;
            }
            default:
            {  Serial.println( String(F("Unknown field type : ")) + MyHandler.MsgType );
               break;
            }
         }
      }
   }
}

Hi deKees,

Het stond allemaal nog aan op de tweede werkbank, dus dat was snel in de Arduino geduuwd!

Nu geen vreemde meldingen meer, behalve dan als ik aan de zendkant de interval heel kort neem, zeg 10mSec.
Dat is verder niet nodig, maar ik weet graag waar de grenzen liggen. :-)

Dan nog dit, als ik de code aan allebij de zijden gelijk maak, heb ik dan twee richting communicatie?
Waarschijnlijk moeten dan wat zaken(variabelen) hernoemt worden neem ik aan.

Dan deze nog:


// ASCII Protocol chars
static const byte STX = 2;
static const byte ETX = 3;

Wat houden de waarden 2 en 3 in, zijn dat de waarden voor hoeveel byte STX en ETX groot moeten zijn?

Dank en groet,
Bram

2 en 3 zijn de ASCII code voor het STX resp ETX character.
Die worden hier gebruikt om het begin en eind van een bericht te markeren.

Het moet wel mogelijk zijn om de code aan beide kanten te laten zenden en ontvangen. Daarvoor moeten beide stukken code samengevoegd worden inderdaad.

Maar het is maar de vraag wat je wilt overbrengen. Vaak is het voldoende om alleen een trigger te sturen. Dan is een enkele byte al voldoende. De code in dit voorbeeld stuurt korte berichten met een commando-byte en een float waarde, dus wat je nodig hebt om een temperatuur over te sturen.

Op zondag 5 mei 2024 18:26:03 schreef SparkyGSX:
Persoonlijk zou ik ervoor kiezen om alleen integers over te sturen, en dan de temperatuur *100 of *1000 te sturen, bij voorkeur met een vaste lengte, met voorloopnullen, dat maakt de ontvangende kant simpel.

Gewoon Json gebruiken. Gemakkelijk om te versturen en gemakkelijk terug te ontcijferen. Zelfs zeer leesbaar in een console als je wil. Verstuur je gewoon de effectieve waarde niks vermenigvuldigen en delen. Hetzelfde format kan je gebruiken om naar een browser te versturen.

Ha, leuk om te zien dat je weer aan het programmeren bent!

JSON kan, maar bedenk wel dat de nano beperkt is in zijn RAM. Dus hou de JSON berichten dan kort. Verder heb je een JSON parser nodig, daar is vast een library voor. Ik weet alleen niet hoe groot dat is en of dat allemaal in die nano past.

@blackdog, het is belangrijk om te weten:

RS232 stuurt bytes één voor één naar het andere apparaat. Het probleem is dat je niet direct kunt zien welke bytes bij welk bericht horen. Je zou kunnen proberen te tellen, maar dit is foutgevoelig. Wat als je door ruis bijvoorbeeld een extra byte ontvangt? Dan worden alle volgende berichten niet correct ontvangen.

Om dit probleem aan te pakken gebruiken we framing. Dat betekent dat we een methode bedenken om het begin en het einde van een bericht (of frame) te detecteren. Er zijn verschillende technieken beschikbaar, elk met hun voor- en nadelen. deKees heeft ervoor gekozen om het begin en einde van een frame aan te duiden met een STX en ETX. Alles tussen deze tekens is de daadwerkelijke data die je wilt verzenden.

Dus, de zender stuurt <STX><DATA><ETX>. De ontvanger werkt als een statemachine: hij wacht tot een STX wordt ontvangen, waarna hij alle binnenkomende bytes in een array opslaat tot een ETX wordt ontvangen.

Deze methode is eenvoudig, maar kent het nadeel dat de data zelf geen STX of ETX mag bevatten. Anders zal de ontvanger deze interpreteren als het begin of einde van een bericht. Er zijn technieken om dit te omzeilen, zoals Byte Stuffing of Consistent Overhead Byte Stuffing, maar dat valt nu buiten de scope. Als je geïnteresseerd bent, kun je daar zeker meer over opzoeken.

Nu we weten hoe we de data als compleet bericht kunnen versturen en ontvangen, moeten we bepalen wat de data betekent. Er zijn verschillende methodes om dit te benaderen.

Je zou kunnen overwegen om gewoon alle bytes achter elkaar te plakken, maar dat is riskant als de bytes per ongeluk een STX of ETX bevatten. Een eenvoudige oplossing is om alle data als string te versturen, zoals 'Temp = 54.24'. Dit vereist dat de ontvangende kant het bericht interpreteert en het getal omzet naar een float.

Een gestandaardiseerde aanpak, zoals het gebruik van JSON, is echter eleganter. Het grote voordeel van een standaard is dat er veel informatie, tools en code beschikbaar zijn. Er bestaat vast en zeker een Arduino-bibliotheek die JSON kan parsen. Met wat onderzoek vind je genoeg informatie over JSON.

Morge hardbass, :-)

Ik heb vanmorgen weer wat uitzoekwerk gedaan, dit omdat het voorbeeld van deKees de twee Interrupt ingangen gebruikt van de 328 chip op mijn controler printje.
Hierdoor kan ik de LMT01 niet uitlezen met de code waar ik mee gestart ben.

De code van de maker van de LMT01 dat is Texas heeft ook een voorbeeld code die gebruik maakt van de A1 ingang waar de comperator aan hangt.
Wat ik mij echter afvraag is of de routine van het uitlezen van de LMT01 sensor via de comparator het data verzenden van de temperatuur naar de andere Arduino
via SoftwareSerial elkaar in de weg zit.

Uit mijn eerste PC ervaringen, denk 1980 ken ik nog de problemen met welke device de hoogste IRQ voorrang had.
Speelt dit hier ook mee?

Mijn er varing met PID versie die ik ga gebruiken en de Arduino is dat de sample frequentie laag is, waarschijnlijk wordt dit een paar keer per seconde.
Dat is het "Window" de waarde uit de sensor bij de hoogste resolutie komt er ongeveer 10x per seconde uit.
Dit is allemaal zo langzaam dat ik niet denk dat er een probleem op gaat treden, maar in ieder geval graag jullie inzicht hier in.

Nog wat extra info betreffende overtjes
In de basis met slechte isolatie maak je de variatie in de oven die je test iets tussen 30x kleiner en max 150 maal kleiner met een zeer goede loop en isolatie t.o.v. de buitenzijde van de oven.

Door nu een dubbele oven te bouwen en je houd voor beide b.v. 50x aan, dan zou je voor de binnen oven aan een waarde komen van 2500x minder variatie.
Jezelf rijk rekenen doe ik niet aan, maar ik hoop 500x te halen.
Er zijn zo veel variabelen die de kwaliteit slechter kunnen maken, zoals het weglekken van energie via bedrading, ongunstige sensor plaatsing, hoe het oventje verwarmd wordt, dus over hoe groot is het deel van de oven behuizing die je verwarmd en dan voor deze oven de PID loop control.

Wat ik al aangaf, dit is ook een leerproces, ik heb hier nu twee binnen ovens liggen die op een andere manier verwarmd worden en er komt nog een derde bij voor de testen.

Om de ontwikkel tijd te beperken heb ik besloten de binnen oven alleen de temperatuur te laten verzenden naar de buitenoven via de code van deKees.
De binnen oven krijgt een Watchdog en ik controleer op de temperatuur die de buiten oven krijgt van de binnen oven of dit tussen bepaalde marges ligt.
Zoniet dan wordt de voeding voor de binnen oven afgeschakeld.

Hiermee hoop ik de dure componenten die op temperatuur moeten blijven in de binnen oven, niet extra gekookt wordt. :+

Texas code voorde LMT01
Hieronder de code voor de LMT01 en deze komt van TI en gebruik de comparator ingang A1 van de Uno of Nano.
De code voor het LCD display haal ik er later uit, deze is niet nodig voor de binnen oven.


#include <LiquidCrystal.h>

//Declare RS, E, D4, D5, D6, D7
LiquidCrystal lcd(3,5,6,9,10,11);

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

void setup() 
{
  //initialize 1602 LCD Display
  lcd.begin(16, 2);
  //Print Label for Pulse count
  lcd.setCursor(0,0);
  lcd.write("PULSES: ");
  //Print label for Temperature
  lcd.setCursor(0,1);
  lcd.write("TEMP(C): ");
  
  //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
}

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 Pulse count to LCD
    lcd.setCursor(9,0);
    lcd.print(pulseCount);
    
    //Print Temperature to LCD
    temperature = 0.0625 * pulseCount - 50;
    lcd.setCursor(9,1);
    lcd.print(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

Mooie uitleg van HardBass. Klopt wel goed.

JSON kan ook natuurlijk, ik gebruik dat zelf ook vaak maar dan wel in javascript in browsers. Hier heb ik geprobeerd om het zo simpel mogelijk te houden.

Ik heb beide scripts nu gecombineerd. Deze nieuwe script kan zowel verzenden als ontvangen. Dus als je deze in beide arduino's zet dan gaan ze elk de data van de ander tonen. Ik ga ervan uit dat je straks toch wel weer 2 aparte scripts krijgt omdat beide kanten andere data willen versturen en de data ook op verschillende manieren gaan aanmaken. Zie het maar als begin.

Nog een paar andere aanpassingen:

  • Andere pin nummers voor de TxD (pin 8) en RxD (pin 9).
  • Het versturen van een bericht is nu een functie-aanroep
  • Naast temperatuur wordt nu ook een teller en een druk verstuurd


#include <SoftwareSerial.h>


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

static const uint32_t Interval = 1000;

// ASCII Protocol chars
static const byte STX = 2;
static const byte ETX = 3;

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;
   }
   
   bool HandleChar(char c)
   {
     if(c == STX)
     {  Setup();
        MsgState = 1;
        return false;
     }
     else if(MsgState == 1)
     {  MsgType  = c;
        MsgState = 2;
        return false;
     }
     else if(MsgState == 2)
     {
       if(c == ETX)
       {  Value /= Exponent;
          return true;
       }
       else if(c == '.')
       {  HasDecimal = true;
          return false;
       }
       else if(isDigit(c))
       {  Value = Value * 10. + (c - '0');
          if(HasDecimal)
          {  Exponent *= 10.;
          }
          return false;
       }
       else
       {  MsgState = 0;  // Invalid char received.
          return false;
       }
     }
     else
     {  MsgState = 0;
        return false;
     }
   }
};

MSG_HANDLER MyHandler;

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

// 
void XmitMessage(char MsgType, float Value)
{  mySerial.write(STX);      // Start of message
   mySerial.write(MsgType);  // To indicate type of value value 
   mySerial.print(Value, 2); // Xmit in ASCII, float with 2 decimals
   mySerial.write(ETX);      // End of message
}


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

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(9600);

   Serial.println("Hello World");
}

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

void loop() 
{  
   
   mySerial.listen();

   static uint32_t Timer = millis();

   if((millis() - Timer) > Interval)
   {  Timer += Interval;
   
      float temp     =   12.7;
      float pressure = 1104.7;
      static float counter = 0.0;

      XmitMessage('C', counter++);
      XmitMessage('T', temp);
      XmitMessage('P', pressure);
   }

   if(mySerial.available())
   { 
      char c = mySerial.read(); // Field Type.
    
      if(MyHandler.HandleChar(c))
      {  
         switch (MyHandler.MsgType)
         {  case 'C':
            {  Serial.println( String(F("Counter : ")) + MyHandler.Value );
               break;
            }
            case 'T':
            {  Serial.println( String(F("Temperature : ")) + MyHandler.Value );
               break;
            }
            case 'P':
            {  Serial.println( String(F("Pressure : ")) + MyHandler.Value );
               break;
            }
            default:
            {  Serial.println( String(F("Unknown field type : ")) + MyHandler.MsgType );
               break;
            }
         }
      }
   }
}

Een echt protocol zit nog wel wat complexer in elkaar. Dan ga je meerdere data velden combineren in een enkel bericht, foutherkenning dmv checksum, en ook timeouts tussen verstuurde bytes inbouwen, en ook terugmelding dat de data is aangekomen, en her-transmissie als dat niet het geval is.

Soms is dat nodig, soms niet.

Op maandag 6 mei 2024 09:33:31 schreef hardbass:
Ha, leuk om te zien dat je weer aan het programmeren bent!

JSON kan, maar bedenk wel dat de nano beperkt is in zijn RAM. Dus hou de JSON berichten dan kort. Verder heb je een JSON parser nodig, daar is vast een library voor. Ik weet alleen niet hoe groot dat is en of dat allemaal in die nano past.

Ik gebruik zelf deze Json library met ATmega328 (even uit het hoofd). Deze verzorgt RS485 communicatie, water temperatuur opvolging (DS18B20), 2 niveau meters, 1 neopixel, 1 alive led (1Hz), 1 relais uitgang (klep sturing) en RTC. Er hangen 9 ATmega's aan de bus (ESP32 als master) zodat er ook nog adres verwerking is. 1 keer per dag stuurt de master een broadcast naar de nodes met het correcte uur. Elke 10 minuten worden de nodes opgevraagd en wordt er watertemperatuur, waterniveau (laag, OK, hoog), toestand klep (open/toe) en RTC temperatuur terug gezonden. Baudrate uit het hoofd 57 000Bd. 7 nodes worden binnen 1 seconde opgevraagd. Antwoord de node niet binnen x seconden wordt de request herhaald en na een 2de poging wordt de node als off line gemeld. Maar dit is zeldzaam en is meestal 10 minuten later verholpen.

Uiteraard is het ontvangstprotocol een solid state zodat de gewone loop niet nodeloos wordt opgehouden. Begin char van de string '{' en CR (0x13) als einde. Je kan ook alleen een JSon decoder gebruiken (bijvoorbeeld van Bodmer). Je bespaart zo waarschijnlijk wat ruimte uit. Een eenvoudige Json string maken is ook geen rocket scene.

Het mooie aan de RS485 met Json is dat je gewoon de bus kan 'sniffen' en op je pc zichtbaar maken in een terminal programma of Arduino console. Je ziet dan zowel de communicatie van master als van slave node.

Edit: gebruikte ATmega is een 644P waarvan de code 42% (28004 bytes) gebruikt en variabelen 34% (1398 bytes) in een 328 moet dat ook lukken. Baudrate 38 400.

Hi,

Dank weer voor de hints en uitleg!

De laatste code van deKees werkt goed! nu de code gaan uitbreiden bij de binnen oven zodat de PID gaat draaien en de code van TI voor de LMT01 testen,
de eerste testen waren met andere code uitgevoerd voor de LMT01.
Het testen bedoel ik eigenlijk dat ik de LMT01 sensor direct aan ingang A1 van de Nano en dan met de scoop pin A1 bekijken of de logische niveaus correct genoeg zijn.
TI geeft in de documentatie die er bij zit aan, dat ik een weerstand van 13,4K moet gebruiken voor "pull down" als ik de sensor uit +5V voed.
Ik wil met de scoop kijken of b.v 10 of 12K ook kan.
Daarbij zal ik de configuratie moeten uitvogelen wat betreft de setup van de comparator.

Dit is een klusje voor vanmiddag, net als een display verbinden aan de Nano voor de buitenoven.
Dus nu komt het er op neer of de code die ik wil gaan gebruiken, elkaar niet in de weg zit. :-)

Groet,
Bram

Uiteraard is het ontvangstprotocol een solid state zodat de gewone loop niet nodeloos wordt opgehouden.

Dit snap ik niet. Ik zou denken dat een 'solid state' juist wel de gewone loop blokkeert tijdens communicatie. Blijkbaar heb jij een andere interpretatie van 'solid state'.

Hier nog wat code, helemaal uitgekleed. Dit is iets wat wij veel gebruiken.

In dit geval zit alles rondom het protocol in de MyProtocolHandler class. Die kan je in een andere file stoppen, dat houd de main lekker overzichtelijk.

Je zou deze ook 2x kunnen maken, eentje die dan praat met de andere arduino en eentje die praat met bijvoorbeeld je PC. Je kunt dan dezelfde commando's aftrappen vanaf de pc als vanaf de tweede nano. Zeer handig als je dingen wilt testen.

Als je interesse hebt, kan ik dit wel compleet maken met de code van deKees.

Trouwens, ipv de <STX><DATA><ETX> zou ik dan een newline als end of message character gebruiken. Dan kan je gewoon met iedere willekeurige terminal applicatie commando's sturen naar je NANO.



void HandleTemperatureMessage(MyProtocolHandler& protocol, char* data, size_t dataLength)
{
    float temperature;
    if(tryGetFloat(data, dataLength, &temperature))
    {
        // Implement your code here, for example, write temperature to the screen.
        
        
        // Let the sender know, command is handled successfully
        protocol.Send("RPLY", true);
    }
    else
    {
        // Let the sender know, something went wrong
        protocol.Send("RPLY", false);
    }
}

// Example using early return.
void HandlePressureMessage(MyProtocolHandler& protocol, char* data, size_t dataLength)
{
    float pressure;
    
    if(!tryGetFloat(data, dataLength, &pressure))
        return;    // Return if we didn't find a float. (Dont sent anything back to sender)

    // Implement your code here, for example, write pressure to the screen.
}


const CommandList = {
    {"TEMP", HandleTemperatureMessage},
    {"PRES", HandlePressureMessage}
};



void main()
{
    MyProtocolHandler protocol;
    protocol.DataInterface = Serial;            // Tell the protocol to use the serial
    protocol.CommandRegister = CommandList;     // Tell the protocol what commands to use
    protocol.Init();
    
    while(1)
    {
        protocol.Handle();
        
    }
}

In interpreteerde het "solid state" maar als "interrupt", dan klopt het een beetje.

Bij text-only is het simpelste om af te sluiten met een <cr>, ook handig voor testen met een terminal programma.

Hi,

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.

Misschien heb ik zoals het vaak gebeurt, iets te veel weg gehaald in de TI code, alles beteffende het LCD display staat uit met //.
( de TI code staat een stukje terug in dit topic )

Verder heb ik Serial.begin(9600); toegevoegt om met Serial.println de data in de monitor weer te kunnen geven.
Er is verder geen andere code aanwezig, alleen dus die voor de LMT01.
Eerst kijken of ik het zelf kan uitvinden en als dat niet lukt zal ik de code die ik test hier laten zien.

hardbass
Even wachten, dit is wel wat nieuw/veel code voor mij om te begrijpen, ik ben geen 18 meer. :+
De laatste code van deKees werkt, ik hoef maar heel weinig te transporteren en in principe traag, zeg 2x per seconde over te zetten.
Eerst de LMT01 sensorcode werkend maken, dan kijken of de PID goed gaat werken, dan wordt de code van deKees toegevoegt en weer testen of het goed gaat.

Later vandaag kom ik hier terug met de resultaten van mijn testjes en of aanvullende vragen.

Dank weer allen voor de input.

Groet,
Bram