資料介紹
描述
如果你有一只物聯(lián)網(wǎng)寵物,它會(huì)吃什么?WiFi SSID,當(dāng)然!
Nerd 是一種無線電子寵物,它通過收集 WiFi SSID 以及一些休息和陽光來生存。
為了讓它茁壯成長,你必須平衡離線和在線模式與光明和黑暗,以確保它有一個(gè)適當(dāng)?shù)娜粘3?睡-書呆子程序。
如果沒有 WiFi 太久,它將使用其內(nèi)置的壓電揚(yáng)聲器以摩爾斯電碼發(fā)送 SOS。離線時(shí)間越長,蜂鳴聲越多。
簡而言之
書呆子將每半小時(shí)醒來一次以掃描周圍的網(wǎng)絡(luò)。如果它檢測(cè)到新網(wǎng)絡(luò),它將存儲(chǔ)它們并在低功耗模式下重新進(jìn)入睡眠狀態(tài)(以節(jié)省電池壽命)。否則它會(huì)用蜂鳴器發(fā)出噪音來抱怨,直到你喂它或把它放在黑暗中。
它還會(huì)通過連接到您的 wifi 網(wǎng)絡(luò)了解何時(shí)在家。在家時(shí),Nerd 將能夠連接到互聯(lián)網(wǎng)并獲取當(dāng)前時(shí)間和日期。
如果超過兩天不喂,它會(huì)急劇死亡,發(fā)出很大的噪音。
成分
- RGB LED
- 蜂鳴器
- 電池
- 220歐姆電阻
學(xué)習(xí)目標(biāo)
- 管理完整的 WiFi 功能
- 在閃存中存儲(chǔ)數(shù)據(jù)
- 管理時(shí)間和實(shí)時(shí)時(shí)鐘
- 管理低功耗模式
想知道更多?
本教程是讓您熟悉 MKR1000 和 IoT 的一系列實(shí)驗(yàn)的一部分。所有實(shí)驗(yàn)都可以使用 MKR IoT Bundle 中包含的組件構(gòu)建。
- 書呆子
設(shè)立董事會(huì)
為了實(shí)現(xiàn)所有功能,我們將使用以下庫:
- WiFi101 //連接到互聯(lián)網(wǎng)并掃描網(wǎng)絡(luò)
- FlashStorage // 保存值,以便它們不會(huì)在每次重新啟動(dòng)時(shí)被擦除
- RTCZero // 管理時(shí)間觸發(fā)事件
- ArduinoLowPower //節(jié)省電池電量
- WiFiUdp // 從互聯(lián)網(wǎng)上獲取時(shí)間和日期
掃描 WiFi 網(wǎng)絡(luò)
書呆子渴望網(wǎng)絡(luò)!
掃描網(wǎng)絡(luò)非常簡單, 只需上傳此示例草圖或轉(zhuǎn)到> 示例 > WiFi101 > ScanNetworksAdvanced以獲得更多擴(kuò)展版本。
#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();
}
在閃存中存儲(chǔ)值
當(dāng)然,您不希望書呆子每次沒電時(shí)就死掉!
為了避免這種行為,我們將在閃存中保存一些變量(作為食物量),以便即使在電路板關(guān)閉并再次打開后也可以檢索它們。
您可以使用以下方法了解基本功能:
示例 > FlashStorage > FlashStoreAndRetrieve
我們將保存網(wǎng)絡(luò)的名稱,以便書呆子只吃一次,我們還將使用保存這些 SSID 的數(shù)組來計(jì)算它在白天吃的食物量。
保存在閃存中的值將在電路板重置后保留,但不會(huì)在新草圖的上傳后保留。每次上傳新草圖時(shí),閃存也會(huì)被清空。
此草圖將掃描網(wǎng)絡(luò)并將 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(); // Read values from flash memory
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) { // check if the SSID name fits 100 bytes
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");
}
}
}
}
}
請(qǐng)注意我們?nèi)绾味x可以保存的最大網(wǎng)絡(luò)數(shù)量,以避免內(nèi)存空間問題:
#define MaxNet 30
int PosToBeSaved = 0;
已用于導(dǎo)航保存網(wǎng)絡(luò)名稱的數(shù)組并測(cè)量已吃的食物量。
管理時(shí)間
我們可以將實(shí)時(shí)時(shí)鐘 (RTC) 的功能與 WiFi 相結(jié)合,以獲取當(dāng)前時(shí)間和日期,然后設(shè)置板的內(nèi)部時(shí)鐘。通過這種方式,我們可以在很長一段時(shí)間內(nèi)觸發(fā)基于時(shí)間的事件,而無需使用millis()
當(dāng)您必須將毫秒轉(zhuǎn)換為天時(shí)可能會(huì)很棘手的函數(shù)。
我們將從網(wǎng)絡(luò)時(shí)間協(xié)議(NTP) 時(shí)間服務(wù)器獲取時(shí)間,然后使用它設(shè)置 RTC。請(qǐng)注意,收到的時(shí)間采用紀(jì)元格式,即自 1970 年 1 月 1 日以來的秒數(shù)。
您可以從example > WiFi101 > WiFiUdpNtpClient運(yùn)行基本草圖
在下面的草圖中,我們將事物放在一起:掃描網(wǎng)絡(luò)功能和與時(shí)間相關(guān)的功能。
-
bool atHome = false;
用于觸發(fā) WiFi 連接,因此請(qǐng)求服務(wù)器獲取時(shí)間。
-
check_home()
用于掃描所有可用網(wǎng)絡(luò)并查看其中一個(gè)是否為家庭 WiFi 網(wǎng)絡(luò)。
-
connect_WiFi()
然后調(diào)用,它將板連接到 WiFi,觸發(fā)對(duì)服務(wù)器的請(qǐng)求并打印當(dāng)前時(shí)間。
-
rtc.setEpoch(epoch + GMT);
用于以紀(jì)元格式以當(dāng)前時(shí)間啟動(dòng) RTC,修改 GMT 變量以將時(shí)間調(diào)整為您當(dāng)前的時(shí)區(qū)。
#include
#include
#include
#include
#include
WiFiUDP udp;
WiFiUDP Udp;
RTCZero rtc;
#define MAGIC_NUMBER 0x7423 // arbitrary number to double check the saved SSID
#define MaxNet 30 // max amount of network to be saved
const char* home_ssid = SECRET_SSID; // your network SSID (name)
const char* password = SECRET_PSWD; // your network password
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
bool atHome = false;
// Struct of variable to be saved in flash memory
typedef struct {
int magic;
boolean valid[MaxNet];
char SSIDs[MaxNet][100];
int alive_days;
int last_time_feeded;
} Networks;
FlashStorage(my_flash_store, Networks);
Networks values;
void setup() {
Serial.begin(115200);
delay(2000);
rtc.begin(); // enable real time clock functionalities
values = my_flash_store.read(); // Read values from flash memory
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() {
if(!atHome) check_home();
// Temporarly save the number of networks
int networks_already_saved = PosToBeSaved;
getNetwork();
if (PosToBeSaved >= daily_amount_of_food) {
Serial.println("Enough food for today");
}
}
void check_home() {
int numSsid = WiFi.scanNetworks();
if (numSsid != -1) {
for (int thisNet = 0; thisNet < numSsid; thisNet++) {
delay(100);
if (strncmp(WiFi.SSID(thisNet), home_ssid, 100) == 0) {
Serial.println("Yay, I'm home \n");
atHome = true;
connect_WiFi();
}
}
}
}
void connect_WiFi() {
if (WiFi.status() != WL_CONNECTED) {
while (WiFi.begin(home_ssid, password) != WL_CONNECTED) {
delay(500);
}
Serial.println("WiFi connected \n");
GetCurrentTime();
printTime();
}
}
// Feed the Nerd with networks's SSID
void getNetwork() {
// scan for nearby networks:
Serial.println("*Scan Networks*");
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) { // check if the SSID name fits 100 bytes
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.last_time_feeded = rtc.getEpoch();
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");
}
}
}
}
}
/*************************************************
Start an UDP connection to get the time in unix,
then set the real time clock (rtc)
************************************************/
unsigned int localPort = 2390; // local port to listen for UDP packets
IPAddress timeServer(129, 6, 15, 28); // time.nist.gov NTP server
const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message
byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets
const int GMT = 1 * 60 * 60; //change this to adapt it to your time zone hours*minutes*seconds
unsigned long epoch;
void GetCurrentTime() {
int numberOfTries = 0, maxTries = 6;
do {
epoch = readLinuxEpochUsingNTP();
numberOfTries++;
}
while ((epoch == 0) || (numberOfTries > maxTries));
if (numberOfTries > maxTries) {
Serial.print("NTP unreachable!!");
while (1);
}
else {
Serial.print("Epoch received: ");
Serial.println(epoch);
rtc.setEpoch(epoch + GMT);
Serial.println();
}
}
unsigned long readLinuxEpochUsingNTP()
{
Udp.begin(localPort);
sendNTPpacket(timeServer); // send an NTP packet to a time server
// wait to see if a reply is available
delay(1000);
if ( Udp.parsePacket() ) {
Serial.println("NTP time received");
// We've received a packet, read the data from it
Udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer
//the timestamp starts at byte 40 of the received packet and is four bytes,
// or two words, long. First, esxtract the two words:
unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
// combine the four bytes (two words) into a long integer
// this is NTP time (seconds since Jan 1 1900):
unsigned long secsSince1900 = highWord << 16 | lowWord;
// now convert NTP time into everyday time:
// Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
const unsigned long seventyYears = 2208988800UL;
// subtract seventy years:
Udp.stop();
return (secsSince1900 - seventyYears);
}
else {
Udp.stop();
return 0;
}
}
// send an NTP request to the time server at the given address
unsigned long sendNTPpacket(IPAddress & address)
{
// set all bytes in the buffer to 0
memset(packetBuffer, 0, NTP_PACKET_SIZE);
// Initialize values needed to form NTP request
// (see URL above for details on the packets)
packetBuffer[0] = 0b11100011; // LI, Version, Mode
packetBuffer[1] = 0; // Stratum, or type of clock
packetBuffer[2] = 6; // Polling Interval
packetBuffer[3] = 0xEC; // Peer Clock Precision
// 8 bytes of zero for Root Delay & Root Dispersion
packetBuffer[12] = 49;
packetBuffer[13] = 0x4E;
packetBuffer[14] = 49;
packetBuffer[15] = 52;
// all NTP fields have been given values, now
// you can send a packet requesting a timestamp:
Udp.beginPacket(address, 123); //NTP requests are to port 123
Udp.write(packetBuffer, NTP_PACKET_SIZE);
Udp.endPacket();
}
void printTime() {
// Print date...
Serial.print(rtc.getDay());
Serial.print(" / ");
Serial.print(rtc.getMonth());
Serial.print(" / ");
Serial.print(rtc.getYear());
Serial.print("\t");
// ...and time
print2digits(rtc.getHours());
Serial.print(": ");
print2digits(rtc.getMinutes());
Serial.print(": ");
print2digits(rtc.getSeconds());
Serial.println("");
}
void print2digits(int number) {
if (number < 10) {
Serial.print("0");
}
Serial.print(number);
實(shí)現(xiàn)當(dāng)前時(shí)間允許我們使用像這樣的簡單函數(shù)來管理 Nerd 的生死:
if(rtc.getEpoch() - values.last_time_fed >= 86400*2){
// complain and eventually die :(
}
其中86400
是一天中的秒數(shù), values.last_time_fed
是以紀(jì)元格式存儲(chǔ)的時(shí)間值,其中 Nerd 上次被喂食并以紀(jì)元格式rtc.getEpoch()
返回當(dāng)前時(shí)間。
低功耗模式
我們可以使用低功耗模式讓我們的電路板進(jìn)入睡眠狀態(tài)。這意味著它將禁用其大部分功能(包括 WiFi)以節(jié)省電池電量。
由于我們希望 Nerd 定期醒來,我們可以輕松設(shè)置一個(gè)計(jì)時(shí)器:
#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;
}
請(qǐng)注意,該WakeUp()
函數(shù)附加到中斷,這意味著它不能包含任何包含延遲的代碼。但是我們可以設(shè)置布爾變量來觸發(fā)循環(huán)中的事件。
- 寵物食品分配器開源設(shè)計(jì)
- 寵物喂食器開源硬件
- 自動(dòng)寵物喂食器開源硬件
- 寵物食品消費(fèi)追蹤器開源分享
- 赫伯特機(jī)器人虛擬寵物開源分享
- HugWear可穿戴寵物開源分享
- 無線電廣播TA2003開源分享
- 自制無線電通信開源項(xiàng)目
- 帶TEF6686的RDS無線電開源
- 無線電控制椰子收割機(jī)開源分享
- 對(duì)于減量AGC無線電開源項(xiàng)目
- 無線電控制車開源項(xiàng)目
- 解析無線電偽碼測(cè)目標(biāo)距離系統(tǒng)的設(shè)計(jì) 0次下載
- 軟件無線電基礎(chǔ) 50次下載
- 軟件無線電RFID測(cè)試平臺(tái)課件下載 32次下載
- 無線電時(shí)鐘的dcf信號(hào)是什么意思 130次閱讀
- 廚房內(nèi)的無線電源解決方案 278次閱讀
- 軟件無線電安全之GNU Radio基礎(chǔ)知識(shí) 3079次閱讀
- 什么是無線電頻譜 最全最新無線通信頻率分配 8359次閱讀
- 兩種常見無線電架構(gòu)對(duì)比 771次閱讀
- X和Ku-頻段小型無線電設(shè)計(jì) 1070次閱讀
- 無線電力傳輸 1972次閱讀
- 無線電信號(hào)的發(fā)送和接收 1.5w次閱讀
- 帶你了解無線電遙控技術(shù) 9902次閱讀
- 軟件無線電架構(gòu)的詳細(xì)概述 1.6w次閱讀
- 基于Zedboard的開源軟件定義無線電設(shè)備——Panoradio 6311次閱讀
- 基于FPGA的軟件無線電平臺(tái)設(shè)計(jì)詳細(xì)教程 3411次閱讀
- 基于SDR技術(shù)的無線電設(shè)計(jì)方法 1533次閱讀
- 軟件定義無線電的創(chuàng)新應(yīng)用 2009次閱讀
- 簡易無線電接收器電路 1.2w次閱讀
下載排行
本周
- 1山景DSP芯片AP8248A2數(shù)據(jù)手冊(cè)
- 1.06 MB | 532次下載 | 免費(fèi)
- 2RK3399完整板原理圖(支持平板,盒子VR)
- 3.28 MB | 339次下載 | 免費(fèi)
- 3TC358743XBG評(píng)估板參考手冊(cè)
- 1.36 MB | 330次下載 | 免費(fèi)
- 4DFM軟件使用教程
- 0.84 MB | 295次下載 | 免費(fèi)
- 5元宇宙深度解析—未來的未來-風(fēng)口還是泡沫
- 6.40 MB | 227次下載 | 免費(fèi)
- 6迪文DGUS開發(fā)指南
- 31.67 MB | 194次下載 | 免費(fèi)
- 7元宇宙底層硬件系列報(bào)告
- 13.42 MB | 182次下載 | 免費(fèi)
- 8FP5207XR-G1中文應(yīng)用手冊(cè)
- 1.09 MB | 178次下載 | 免費(fèi)
本月
- 1OrCAD10.5下載OrCAD10.5中文版軟件
- 0.00 MB | 234315次下載 | 免費(fèi)
- 2555集成電路應(yīng)用800例(新編版)
- 0.00 MB | 33566次下載 | 免費(fèi)
- 3接口電路圖大全
- 未知 | 30323次下載 | 免費(fèi)
- 4開關(guān)電源設(shè)計(jì)實(shí)例指南
- 未知 | 21549次下載 | 免費(fèi)
- 5電氣工程師手冊(cè)免費(fèi)下載(新編第二版pdf電子書)
- 0.00 MB | 15349次下載 | 免費(fèi)
- 6數(shù)字電路基礎(chǔ)pdf(下載)
- 未知 | 13750次下載 | 免費(fèi)
- 7電子制作實(shí)例集錦 下載
- 未知 | 8113次下載 | 免費(fèi)
- 8《LED驅(qū)動(dòng)電路設(shè)計(jì)》 溫德爾著
- 0.00 MB | 6656次下載 | 免費(fèi)
總榜
- 1matlab軟件下載入口
- 未知 | 935054次下載 | 免費(fèi)
- 2protel99se軟件下載(可英文版轉(zhuǎn)中文版)
- 78.1 MB | 537798次下載 | 免費(fèi)
- 3MATLAB 7.1 下載 (含軟件介紹)
- 未知 | 420027次下載 | 免費(fèi)
- 4OrCAD10.5下載OrCAD10.5中文版軟件
- 0.00 MB | 234315次下載 | 免費(fèi)
- 5Altium DXP2002下載入口
- 未知 | 233046次下載 | 免費(fèi)
- 6電路仿真軟件multisim 10.0免費(fèi)下載
- 340992 | 191187次下載 | 免費(fèi)
- 7十天學(xué)會(huì)AVR單片機(jī)與C語言視頻教程 下載
- 158M | 183279次下載 | 免費(fèi)
- 8proe5.0野火版下載(中文版免費(fèi)下載)
- 未知 | 138040次下載 | 免費(fèi)
評(píng)論
查看更多