/* Author: Kåre-Benjamin Hammervold Rørvik og Nils Kristian Rossing 23.9.2020 On behald of NTNU. Edited 27.12.2020 En ESP32 som står i Station Mode og fungerer som uplink og gateway. Mottar datapakker over ESP-NOW. Sender og mottar data over WiFi opp mot CoT. Bytter mellom WiFi og ESP-NOW. Antall meldinger sendt vises på displayet, temperatur og fuktighet i rotering. Pinout: 7-Segment | ESP-32 ---------------------------------- CLK | D32 DIO | D33 VDD: | 3V3 (3.3V) GND: | GND // Sources: https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_now.html https://randomnerdtutorials.com/esp-now-esp8266-nodemcu-arduino-ide/?fbclid=IwAR0pH-ilF45ceHlyqjB1Im5oDkOJUtxJ7g1TDABbaSOkiBep5xYlsH1y5_M */ #include #include #include #include #include #define displayDividerBitMask 0b01000000 // 7-segment display const byte PIN_CLK = 32; const byte PIN_DIO = 33; TM1637Display display(PIN_CLK, PIN_DIO); // Lager et array av typen byte (byte = 8bit). // Dette brukes ofte for å styre 7-segment display. // Merk: Displayene har enten 7 eller 8 LED segment, som akkurat går opp i en byte. const byte UP[] = { SEG_B | SEG_C | SEG_D | SEG_E | SEG_F, // U SEG_A | SEG_B | SEG_E | SEG_F | SEG_G, // P 0, 0, }; /* Disse deklarasjonene inneholder informasjonen som er nødvendig for å koble opp mot skyen og ta i bruk de ønskede funksjonalitetene. Når en bruker char på denne måten opprettes en vektor, som senere kan leses ut (i motsettning til string som er litt enklere å ta i bruk). Det er påkrevd å bruke dette formatet for å kunne kommunisere med skytjenesten til CoT. */ char ssid[] = ""; // Legg in navnet på nettverket ditt her. Denne verdien er Case sensitivt (den skiller mellom små og store bokstaver). char password[] = ""; // Legg in nettverkspassordet ditt her (denne er også Case sensitiv). char server[] = "www.circusofthings.com"; // Addressen til tjenesten vi skal kommunisere med [Trenger ikke endres] char temperature_key[] = ""; // Place the Key of the signal you created at Circus Of Things for the Temperature char humidity_key[] = ""; // Place the Key of the signal you created at Circus Of Things for the Humidity char pressure_key[] = ""; // Place the Key of the signal you created at Circus Of Things for the Preassure char order_key[] = ""; // Type the Key of the Circus Signal you want the ESP32 listen to. char token[] = ""; // Dette er din login token, den finner du på kontoen din (Account). // Her lages et objekt og vi får tilgang til dets funksjoner. // Grunnen til at vi tar inn informasjonen om nettet her, er at vi ikke trenger å legge det inn flere ganger andre steder. // Disse har nå vi gitt objektet tilgang på allerde (og slik er det definert av CoT på forhånd). CircusESP32Lib circusESP32(server,ssid,password); // Transmitted data typedef struct { int instance; float temperature; float humidity; float pressure; String source; } ESPNOWData; // Oppretter et packet objekt for strukturen ESPNOWData. ESPNOWData packet; // Denne variabelen brukes i switch/case for å angi hvilken state som blir den neste. // Vi starter på 0, altså default case. int nextState = 0; void setup() { Serial.begin(115200); initDisplays(); initESPNOW(); } void loop() { //uploadTheData(20000); } void initDisplays(){ // Initialises the displays. // 7-segment directly from I/O. display.setBrightness(7); pinMode(LED_BUILTIN, OUTPUT); } void initESPNOW() { // Init ESP-NOW med en promise structure. // ESP-32 må være konfigurert i Station Mode for at ESP-NOW skal kunne benyttes. WiFi.mode(WIFI_STA); // Funksjoner som aktiveres og returnerer en status: // Disses statusene er de overnevnte. // esp_now_init() vil returnere enten ESP_OK eller ESP_ERR_ESPNOW_INTERNAL // Disse antyder om ESP-NOW er konfigurert korrrekt eller ikke. // ESP_OK betyr at alt er riktig konfigurert, ESP_ERR_ESPNOW_INTERNAL er en feilmelding. // Den kan sees på som aktiv lav: ESP_OK = 0 = LOW, ESP_ERR_ESPNOW_INTERNAL = 1 = HIGH. int initStatus = esp_now_init(); // esp_now_register_recv_cb aktiverer callback funksjonen "activateOnCallback" når ESP-32 får inn en datapakke. // Funksjonen returnere også status slik som esp_now_init gjør. int callbackStatus = esp_now_register_recv_cb(activateOnCallback); // En serie med logikk som brukes for å angi om ESP-NOW er riktig konfigurert og er klar til å brukes. // Dersom noe går galt vil ESP-32 bli restartet. Serial.println("---------------ESP-NOW-----------------"); if (initStatus == ESP_OK){ Serial.println("ESP-NOW er konfigurert riktig"); if (callbackStatus == ESP_OK){ Serial.println("... og Callback funksjonen blir kalt"); } else { Serial.println("... Noe gikk galt etter konfigureringen, kanskje callback deklareringen?"); Serial.println("Programmet restartes etter ti sekunder"); delay(10000); ESP.restart(); } Serial.println("ESP-NOW er klar til bruk"); } else { Serial.println("ESP-NOW er ikke konfigurert riktig, programmet restartes etter ti sekunder"); delay(10000); ESP.restart(); } Serial.println("----------------------------------------"); } void writeToDisplays(float dataToPrint, bool decimalNumber = false){ // Skriver data fra dataToPrint variabelen til 7-segment display // Det alternative argumentet decimalNumber styrer hvordan displayet viser data. // Ved false så tiplasses displayet 2 siffer før og etter kommaet.(ved hjelp av calcualteIntcustomForm funksjonen) // Ellers skriver displayet som vanlig. if(decimalNumber){ display.showNumberDecEx(calculateIntCustomForm(dataToPrint,2),displayDividerBitMask); // DisplayDividerBitMask brukes for å angi at displayet skal vise en ":" på midten av displayet. // Dette brukes vi for å skille mellom heltall og desimal-delen av tallet. } else { display.showNumberDec(dataToPrint); // Skriver ut tallet på vanlig vis (uten skillet på midten). } } int calculateIntCustomForm(float floatingInput, int numberOfDecimals) { // Denne funksjonen tar inn en float (flyttall) med n (numberOfDecimals) siffer presisjon etter kommaet. // Funksjonen returnerer heltallet og desimaldelen som et sammenslått intNumber. // Ved flere desimaler kan avrundingsfeil bli større. // Dersom H er et heltall og D er desimaldelen vil det ta formen: HHDD ved 2 siffer før og etter kommaet. // F.eks.: vil 22,54 se ut som 2254 altså uten komma. // Funksjonen ville da være egnet for input (floatingInput) i området -9,99 til 99,99. int offset = pow(10,numberOfDecimals); // Dersom verdien er større enn 0 må den justeres for avrundingsfeil (konverteringer rundes opp) if (floatingInput == 0){ int intNumber = 0; return intNumber; } // Dersom verdien ikke er null justerer vi den direkte. else { int intNumber = floatingInput * offset; return intNumber; } } void uploadTheData(int delayLength){ // Laster opp data til CoT. // Tar i mot en delayLength som bestemmer hvor ofte data skal lastes opp til skyen. digitalWrite(LED_BUILTIN, HIGH); display.setSegments(UP); circusESP32.begin(); // Let the Circus object set up itself for an SSL/Secure connection circusESP32.write(temperature_key,packet.temperature,token); // Report the temperature measured to Circus. circusESP32.write(humidity_key,packet.humidity,token); // Report the humidity measured to Circus. circusESP32.write(pressure_key,packet.pressure,token); // Report the pressure measured to Circus. digitalWrite(LED_BUILTIN, LOW); delay(10); WiFi.disconnect(true); WiFi.mode(WIFI_STA); delay(delayLength); } void activateOnCallback(const uint8_t * mac, const uint8_t *incomingData, int len) { // Tar i mot incomingData og lagrer en kopi til packet objektet. // Receives the incoming data and writes to terminal. // len og mac argumentene brukes ikke direkte i vår funksjon, men må være med i argument listen da denne formen angitt av Espressif (ESP-NOW). // Memcpy koppierer verdiene i incomingData til minneblokken som vi lagrer packet i. // Data kopieres fra incomingData objektet til packet objektet. // Sizeof operasjonen angir antall bytes som utgjør packet, // ... og denne informasjonen brukes i memcpy for å bestimme antallet bytes som skal kopieres. memcpy(&packet, incomingData, sizeof(packet)); Serial.print("Data packet nummer: "); Serial.print(packet.instance); Serial.print(" fra: "); Serial.println(packet.source); Serial.print("Temperaturen er: "); Serial.print(packet.temperature); Serial.println("C"); Serial.print("Luftfuktigheten er : "); Serial.print(packet.humidity); Serial.println("%"); Serial.print("Pressure is : "); Serial.print(packet.pressure/100); Serial.println("hPa"); Serial.println(); switch (nextState) { case 1: writeToDisplays(packet.humidity, true); nextState++; break; case 2: writeToDisplays(packet.temperature, true); nextState++; break; case 3: writeToDisplays(packet.pressure/100); // deler på 100 og får dermed enheten hPa (hecto Pascal) // Displayet viser bedre verdier som ikke blir for store. nextState = 0; break; default: //printAmounOfTransfers writeToDisplays(packet.instance); nextState++; break; } }