×

带MKR WiFi 1010的书呆子

消耗积分:0 | 格式:zip | 大小:0.14 MB | 2022-12-07

百灵千岛酱

分享资料个

描述

如果你有一只物联网宠物,它会吃什么?当然是 WiFi SSID!

Nerd 是一种无线电子宠物,通过收集 WiFi SSID 以及一些休息和阳光来生存。为了让它茁壮成长,你必须平衡离线和在线模式以及明暗,以确保它有一个适当的日常饮食 - 睡眠 - 书呆子例程。如果它长时间没有连接 WiFi,它会使用其内置的压电扬声器以摩尔斯电码发送 SOS。脱机时间越长,蜂鸣声越多。

简而言之

Nerd 每半小时醒来一次,扫描它周围的网络。如果它检测到新网络,它将存储它们并以低功耗模式返回睡眠状态(以节省电池寿命)。否则它会通过蜂鸣器发出噪音来抱怨,直到你喂它或把它放在黑暗中。它还会通过连接到您的 wifi 网络了解它何时在家。当在家时,书呆子将能够连接到互联网并获取当前时间和日期。如果超过两天不喂食,它会急剧死亡,发出很大的声音。

成分

  • 红绿灯
  • 光电晶体管
  • 蜂鸣器
  • 电池
  • 220欧姆电阻

学习目标

  • 管理完整的 WiFi 功能
  • 在闪存中存储数据
  • 管理时间和实时时钟
  • 管理低功耗模式

想知道更多?

本教程是让您熟悉 MKR1010 和物联网的一系列实验的一部分。所有实验都可以使用 MKR IoT Bundle 中包含的组件来构建。

设置董事会

为了实现所有功能,我们将使用以下库:

  • WiFiNINA //连接到互联网并扫描网络
  • FlashStorage // 保存值,这样它们就不会在每次重启时被删除
  • RTCZero // 管理时间触发事件
  • ArduinoLowPower // 节省电池电量
  • WiFiUdp // 从互联网获取时间和日期

您可以按照本指南中的说明从库管理器下载它们。

扫描 WiFi 网络

书呆子渴望网络!扫描网络非常简单, 只需上传此示例草图或转到> 示例 > WiFiNINA > ScanNetworksAdvanced以获得更扩展的版本。

#include 
#include 
void setup()
 {
  //Initialize serial and wait for port to open:
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }  // scan for existing networks:
  Serial.println();
  Serial.println("Scanning available networks...");
  listNetworks();
 }
 void loop()
 {
  delay(10000);  // scan for existing networks:
  Serial.println("Scanning available networks...");
  listNetworks();
 }
 void listNetworks()
 {  // scan for nearby networks:
  Serial.println("** Scan Networks **");
  int numSsid = WiFi.scanNetworks();
  if (numSsid == -1)
  {
    Serial.println("Couldn't get a WiFi connection");
    while (true);
  }  // print the list of networks seen:
  Serial.print("number of available networks: ");
  Serial.println(numSsid);
  // print the network number and name for each network found:
  for (int thisNet = 0; thisNet < numSsid; thisNet++) {
    Serial.print(thisNet + 1);
    Serial.print(" SSID: ");
    Serial.println(WiFi.SSID(thisNet));
    Serial.flush();
  }
  Serial.println();
 } 

在闪存中存储值

你肯定不希望 Nerd 每次没电时都死掉!为了避免这种行为,我们将在闪存中保存一些变量(如食物量),以便即使在电路板转动后也可以检索它们关闭并再次打开。您可以通过以下方式了解基本功能:

示例 > FlashStorage > FlashStoreAndRetrieve

我们将保存网络的名称,以便 Nerd 只吃一次,我们还将使用保存这些 SSID 的数组来计算它白天吃的食物量。保存在闪存中的值将保留下来重置电路板但不上传新草图。每次上传新草图时,闪存也会被清空。此草图将扫描网络并将 SSID 保存在闪存中。

#include 
#include 
#include 
#define MAGIC_NUMBER 0x7423  // arbitrary number to double check the saved SSID 
#define MaxNet 30  // max amount of network to be saved  
int PosToBeSaved = 0; // Variable used to navigate the array of networks 
int daily_amount_of_food = 12; // The amount of food per day needed to survive 
// Struct of variable to be saved in flash memory 
typedef struct {
  int magic;
  boolean valid[MaxNet];
  char SSIDs[MaxNet][100];
 } Networks;
 FlashStorage(my_flash_store, Networks);
 Networks values;
 void setup() {
  Serial.begin(115200);
  while(!Serial); // wait until the Serial montior has be opened
  delay(2000);
  values = my_flash_store.read();
  if (values.magic == MAGIC_NUMBER) { // If token is correct print saved networks
    Serial.println("saved data:");
    Serial.println("");
    for (int a = 0; a < MaxNet; a++) {
      if (values.valid[a]) {
        Serial.println(values.SSIDs[a]);
      } else {
        PosToBeSaved = a;
      }
    }
  }
 }
 void loop() {  // Temporarly save the number of networks
  int networks_already_saved = PosToBeSaved;
  getNetwork();
  if (PosToBeSaved >= daily_amount_of_food) {
    Serial.println("Enough food for today");
  }
  delay(5000);
 }
 // Feed the Nerd with networks's SSID
 void getNetwork() {  // scan for nearby networks:
  Serial.println("\n*Scan Networks*\n");
  int numSsid = WiFi.scanNetworks();
  delay(1000);
  if (numSsid == -1)  {
    Serial.println("There are no WiFi networks here..");
  } else {
    Serial.print("number of available networks: ");
    Serial.println(numSsid);
    // print the network number and name for each network found:
    for (int thisNet = 0; thisNet < numSsid; thisNet++) {
      Serial.print("SSID: ");
      Serial.println(WiFi.SSID(thisNet));
      delay(500);
      char* net = WiFi.SSID(thisNet);
      bool canBeSaved = true;
      // check if the network has already been saved
      for (int a = 0; a < PosToBeSaved ; a++) {
        if (values.valid[a]) {
          if (strncmp(net, values.SSIDs[a], 100) == 0 || strnlen(net, 100) == 0) {
            Serial.println("Not saved");
            canBeSaved = false;
          }
        }
      }
      // Store ssid name
      if (canBeSaved && PosToBeSaved < MaxNet) {
        if (strlen(net) + 1 < 100 && strlen(net) > 0) {
          memset(values.SSIDs[PosToBeSaved], 0, sizeof(values.SSIDs[PosToBeSaved])); // set all characters to zero
          memcpy(values.SSIDs[PosToBeSaved], net, strlen(net) + 1); // copy "net" to values.SSDs[thisNet]
          values.valid[PosToBeSaved] = true;
          values.magic = MAGIC_NUMBER;
          my_flash_store.write(values);
          Serial.println(String(values.SSIDs[PosToBeSaved]) + " saved in position " + String(PosToBeSaved));
          PosToBeSaved ++;
        }
        else {
          Serial.println(" network skipped");
        }
      }
    }
  }
 } 

请注意我们如何定义可以保存的最大网络数量,以避免内存空间问题:

#define MaxNet 30  
int PosToBeSaved = 0; 

已用于导航保存网络名称的数组并测量已经吃掉的食物量。

管理时间

我们可以将实时时钟 (RTC) 的功能与 WiFi 相结合,以获取当前时间和日期,然后设置电路板的内部时钟。通过这种方式,我们可以在很长一段时间内触发基于时间的事件,而无需使用millis()将毫秒转换为天时可能会很棘手的功能。我们将从网络时间协议(NTP) 时间服务器获取时间,然后设置 RTC用它。请注意,收到的时间采用纪元格式,即自 1970 年 1 月 1 日以来的秒数。您可以从示例 > WiFiNINA> WiFiUdpNtpClient运行基本草图在下面的草图中,我们将所有内容放在一起:扫描网络功能和时间相关的。

  • bool atHome = false; 用于触发 WiFi 连接,因此请求服务器获取时间。
  • check_home() 用于扫描所有可用网络,并查看其中一个是否是家庭 WiFi 网络。
  • connect_WiFi()然后调用,它会将开发板连接到 WiFi,触发对服务器的请求并打印当前时间。
  • rtc.setEpoch(epoch + GMT);用于以纪元格式的当前时间启动 RTC,修改 GMT 变量以将时间调整为您当前的时区。
#include 
#include 
#include 
#define MAGIC_NUMBER 0x7423  // arbitrary number to double check the saved SSID 
#define MaxNet 30  // max amount of network to be saved  
int PosToBeSaved = 0; // Variable used to navigate the array of networks 
int daily_amount_of_food = 12; // The amount of food per day needed to survive 
// Struct of variable to be saved in flash memory 
typedef struct {
  int magic;
  boolean valid[MaxNet];
  char SSIDs[MaxNet][100];
 } Networks;
 FlashStorage(my_flash_store, Networks);
 Networks values;
 void setup() {
  Serial.begin(115200);
  while(!Serial); // wait until the Serial montior has be opened
  delay(2000);
  values = my_flash_store.read(); // Read values from flash memory
  if (values.magic == MAGIC_NUMBER) {
    Serial.println("saved data:");
    Serial.println("");
    for (int a = 0; a < MaxNet; a++) {
      if (values.valid[a]) {
        Serial.println(values.SSIDs[a]);
      } else {
        PosToBeSaved = a;
      }
    }
  }
 }
 void loop() {  // Temporarly save the number of networks
  int networks_already_saved = PosToBeSaved;
  getNetwork();
  if (PosToBeSaved >= daily_amount_of_food) {
    Serial.println("Enough food for today");
  }
  delay(5000);
 }
 // Feed the Nerd with networks's SSID 
void getNetwork() {  // scan for nearby networks:
  Serial.println("\n*Scan Networks*\n");
  int numSsid = WiFi.scanNetworks();
  delay(1000);
  if (numSsid == -1)  {
    Serial.println("There are no WiFi networks here..");
  } else {
    Serial.print("number of available networks: ");
    Serial.println(numSsid);
    // print the network number and name for each network found:
    for (int thisNet = 0; thisNet < numSsid; thisNet++) {
      Serial.print("SSID: ");
      Serial.println(WiFi.SSID(thisNet));
      delay(500);
      char* net = WiFi.SSID(thisNet);
      bool canBeSaved = true;
      // check if the network has already been saved
      for (int a = 0; a < PosToBeSaved ; a++) {
        if (values.valid[a]) {
          if (strncmp(net, values.SSIDs[a], 100) == 0 || strnlen(net, 100) == 0) {
            Serial.println("Not saved");
            canBeSaved = false;
          }
        }
      }
      // Store ssid name
      if (canBeSaved && PosToBeSaved < MaxNet) {
        if (strlen(net) + 1 < 100 && strlen(net) > 0) {
          memset(values.SSIDs[PosToBeSaved], 0, sizeof(values.SSIDs[PosToBeSaved]));
          memcpy(values.SSIDs[PosToBeSaved], net, strlen(net) + 1);
          values.valid[PosToBeSaved] = true;
          values.magic = MAGIC_NUMBER;
          my_flash_store.write(values);
          Serial.println(String(values.SSIDs[PosToBeSaved]) + " saved in position " + String(PosToBeSaved));
          PosToBeSaved ++;
        }
        else {
          Serial.println(" network skipped");
        }
      }
    }
  }
 } 

实现当前时间允许我们使用像这样的简单函数来管理 Nerd 的生死:

 if(rtc.getEpoch() - values.last_time_fed >= 86400*2){
 // complain and eventually die :(
 } 

其中86400是一天中的秒数, 是书呆子最后一次被喂食的纪元格式values.last_time_fed 时间的存储值,并以纪元格式返回当前时间rtc.getEpoch()

低功耗模式

我们可以使用低功耗模式让我们的电路板进入睡眠状态。这意味着它将禁用大部分功能(包括 WiFi)以节省电池电量。由于我们希望书呆子定期醒来,我们可以轻松设置一个计时器:

#include "ArduinoLowPower.h" 
int sleeping_time = 5000; // 5 seconds 
bool awake = true;
void setup() {
  Serial.begin(9600);
  LowPower.attachInterruptWakeup(RTC_ALARM_WAKEUP, WakeUp, CHANGE);
  pinMode(LED_BUILTIN, OUTPUT);
 }
 void loop() {
  if (awake) {
    digitalWrite(LED_BUILTIN, HIGH);
    delay(500);
    digitalWrite(LED_BUILTIN, LOW);
    delay(500);
  }
  awake = false;
  Serial.println("going to sleep");
  LowPower.sleep(sleeping_time);
 }
 void WakeUp() {
  Serial.println("awake");
  awake = true;
 } 

请注意,该WakeUp()函数附加到中断,这意味着它不能包含任何包含延迟的代码。但是我们可以设置 s 布尔变量 so 来触发循环中的事件。


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

评论(0)
发评论

下载排行榜

全部0条评论

快来发表一下你的评论吧 !