資料介紹
描述
呼呼呼……圣誕老人來了!
每年平安夜圣誕老人都會踏上環游世界的旅程,為全世界的孩子們送出禮物。但是知道那天圣誕老人在哪里會很酷嗎?
MKR1000 圣誕老人追蹤器來救援!
您可能已經知道我們可以從兩個地方獲取圣誕老人的位置信息,一個來自NORAD ,一個來自Google 。雖然 NORAD 是發起圣誕老人追蹤傳統的原始組織,但谷歌為圣誕老人追蹤提供了一個開發人員友好的(未記錄的)API 。使用此 API,您將能夠跟蹤實時圣誕老人信息,包括位置、到達和離開時間、在該位置發送的禮物,與在 Google 的圣誕老人跟蹤網站上相同。所以在這個項目中我選擇使用谷歌圣誕老人數據來實現我的圣誕老人追蹤器。
這個項目的想法很簡單:使用 LED 顯示圣誕老人去過的地方以及圣誕老人在世界地圖上的當前位置。這是我在項目結束時得到的:
系統架構
總體而言,該設計使用一個樹莓派和一個 MKR1000 來處理和可視化從 Google Santa Tracker API 獲取的圣誕老人數據。
如您所見,從 Google Santa Tracker API 獲取的數據首先通過 Raspberry PI 放置。原因是 API 響應 JSON 大約為 20M,這太大而無法放入 MKR1000 的內存(我的草圖加載后有 18M 可用)進行處理。所以我使用 Raspberry Pi 3 首先使用數據,并生成一個更小的數據格式,這是為我的應用程序量身定做的。之后的數據通過托管在 Raspberry Pi 上的 REST API 服務器公開。MKR1000 開發板將每 10 秒調用一次 REST API 以獲取當前圣誕老人的位置。
電路設計
MKR1000 連接到具有 30 個 WS2812B RGB NeoPixel LED 的定制 PCB。每個 LED 代表一個地理位置。這個想法是讓離圣誕老人當前位置最近的 LED 燈閃爍,然后打開圣誕老人過去所在位置的所有 LED 燈。
PCB 針對由 Othermill 生產的雙面 PCB 進行了優化,但也應該可以通過 OSH Park 等在線 PCB 服務輕松生產。
我在PCB上鋪了30個NeoPixels,形成了一張世界地圖。您可能現在看不到它,但是在涂上負片掩模和擴散器后,它會更容易識別。
使這種家庭銑削友好的主要考慮因素是通孔的位置。由于過孔是在 PCB 板上鉆的,因此兩側未連接。因此,您需要通過電線將它們的兩側焊接在一起。正因為如此,通孔不能像在商業 PCB 中通常那樣放置在 SMT 組件下方。
PCB 文件背后有一些設計注意事項:
- 電源通過粗干線傳輸并下沉到另一根粗地線中。這是因為 30 個 NeoPixel 會消耗相當多的電流。
- NeoPixels 盡可能靠近主干電源線連接。這是為了減少多個 NeoPixel 之間的壓降。不同的電壓會導致亮度和顏色略有不同。
- 靠近電源連接一個 1000 uF 電容器。這是使用 NeoPixels的推薦最佳實踐。
- 過孔放置在其他 SMT 組件之外,因為它們將通過手動焊接兩側連接,焊點不會適合另一個 SMT 組件。這在商業服務生產的 PCB 上不是問題,但在家庭制作 PCM 時應考慮在內。
- 確保在電線之間留出足夠的空間。 焊接后很容易短路,因為自制的PCB沒有絕緣層。這可以在 Otherplan 應用程序中通過將跡線間隙設置為比默認值 0.006 英寸更大的值(例如 0.06 英寸應該足夠好)來完成。我發現我的第一塊完全焊接的電路板因為短路而無法正常工作,而且修復起來比制作另一塊電路板更難。
- 如果您要處理大量 SMT 組件,熱風返修站將為您節省大量焊接時間。雖然這是可行的,但真的不值得一個一個地手工焊接組件。熱風也會使組件自動對齊到準確的位置。
世界地圖面具和擴散器
因為 Othermill 可以直接從 SVG 進行銑削,所以我只使用從 wikimedia.org 找到的世界地圖。
我還創建了另一個包含鉆孔的鷹文件。這些孔用于將掩模安裝到電路板上。
因為我想獲得盡可能高的銑削精度,所以最好的方法是使用對齊支架。但是輪廓肯定會和括號重疊。我找到的解決方案是首先只切割地圖而不切割輪廓。然后在不告訴軟件的情況下取下支架。然后開始銑削以鉆孔并切割輪廓。該軟件將使用相同的刀具路徑在您想要的精確位置切割輪廓。
組裝非常簡單。我使用了 4 個 M3 尼龍長螺釘、4 個支架和 4 個螺母。
樹莓派預處理服務器
Google Santa Tracker API 的響應是一個巨大的 JSON 文件。這對 MKR1000 來說可能是個問題,但對 Raspberry Pi 3 來說根本不是問題。所以我設置了一個 HTTP 服務器來預處理 JSON 文件并為 MKR1000 生成較小的數據。
Raspberry Pi 服務器還將位置直接映射到相應 LED 的索引,以進一步減少 MKR1000 上的計算。為此,我首先手動為每個 LED 分配一個坐標,然后計算圣誕老人路徑中的每個位置與 LED 坐標之間的距離,找到最近的 LED 來表示該位置。
服務器是用 Python 編寫的,并使用 Flask 網絡框架將 REST 端點公開給 MKR1000。
from flask import Flask
import requests
import json
import math
import sys
app = Flask(__name__)
# Google's Santa API. Only updates on Dec 24.
# santa_api_url = 'https://santa-api.appspot.com/info?client=web&language=en&fingerprint=&routeOffset=0&streamOffset=0'
# My Fake Santa API.
santa_api_url = 'http://localhost:1224/info'
# LEDs metadata.
leds = [
{'name': 'North Pole', 'location': {'lat': 90.0, 'lng': 30.0}},
{'name': 'Alaska (US)', 'location': {'lat': 64.536117, 'lng': -151.258768}},
{'name': 'Alberta (Canada)', 'location': {'lat': 48.9202307, 'lng': -93.69738}},
{'name': 'Ontario (Canada)', 'location': {'lat': 50.956252, 'lng': -87.369255}},
{'name': 'Utah (US)', 'location': {'lat': 40.7765868, 'lng': -111.9905244}},
{'name': 'Tennessee (US)', 'location': {'lat': 36.1865589, 'lng': -86.9253274}},
{'name': 'Mexico City (Mexico)', 'location': {'lat': 19.39068, 'lng': -99.2836957}},
{'name': 'Bogota (Columbia)', 'location': {'lat': 4.6482837, 'lng': -74.2478905}},
{'name': 'Brasilia (Brazil)', 'location': {'lat': -15.721751, 'lng': -48.0082759}},
{'name': 'Santiago (Chile)', 'location': {'lat': -33.4727092, 'lng': -70.7699135}},
{'name': 'Greenland', 'location': {'lat': 70.8836652, 'lng': -59.6665893}},
{'name': 'UK', 'location': {'lat': 64.6748061, 'lng': -7.9869018}},
{'name': 'Spain', 'location': {'lat': 40.4379332, 'lng': -3.749576}},
{'name': 'Mali', 'location': {'lat': 17.5237416, 'lng': -8.4791157}},
{'name': 'Finland', 'location': {'lat': 64.6479136, 'lng': 17.1440256}},
{'name': 'Greece', 'location': {'lat': 38.2540419, 'lng': 21.56707}},
{'name': 'Libya', 'location': {'lat': 21.520733, 'lng': 23.237173}},
{'name': 'Central African Republic', 'location': {'lat': 6.2540984, 'lng': -0.2809593}},
{'name': 'Botswana', 'location': {'lat': -22.327399, 'lng': 22.4437318}},
{'name': 'Saudi Arabia', 'location': {'lat': 24.0593214, 'lng': 40.6158589}},
{'name': 'Turkmenistan', 'location': {'lat': 38.9423384, 'lng': 57.3349508}},
{'name': 'Xinjiang (China)', 'location': {'lat': 42.0304225, 'lng': 77.3185349}},
{'name': 'India', 'location': {'lat': 20.8925986, 'lng': 73.7613366}},
{'name': 'Henan (China)', 'location': {'lat': 33.8541479, 'lng': 111.2634555}},
{'name': 'Cambodia', 'location': {'lat': 12.2978202, 'lng': 103.8594626}},
{'name': 'Japan', 'location': {'lat': 34.452585, 'lng': 125.382845}},
{'name': 'Australia', 'location': {'lat': -25.0340388, 'lng': 115.2378468}},
{'name': 'New Zealand', 'location': {'lat': -43.0225411, 'lng': 163.4767905}},
{'name': 'South Pole', 'location': {'lat': -90.0, 'lng': 30.0}},
]
@app.route('/santa')
def santa():
santa_info = requests.get(santa_api_url).json()
santa_time = santa_info['now']
response = []
for dest_json in santa_info['destinations']:
if santa_time < dest_json['arrival']:
break
dist, led, led_index = closest_led(dest_json['location'])
response.append({
'i': led_index,
'd': int(dist),
'n': dest_json['city'],
'p': dest_json['presentsDelivered']
})
return app.response_class(json.dumps(response).replace(' ',''), content_type='application/json')
def distance(loc1, loc2, unit='M'):
lat1 = loc1['lat']
lng1 = loc1['lng']
lat2 = loc2['lat']
lng2 = loc2['lng']
radlat1 = math.pi * lat1 / 180
radlat2 = math.pi * lat2 / 180
theta = lng1-lng2
radtheta = math.pi * theta / 180
dist = (math.sin(radlat1) * math.sin(radlat2) +
math.cos(radlat1) * math.cos(radlat2) * math.cos(radtheta));
dist = math.acos(dist)
dist = dist * 180 / math.pi
dist = dist * 60 * 1.1515
if unit == 'K':
return dist * 1.609344
if unit == 'N':
return dist * 0.8684
return dist
def closest_led(loc):
min_dist = sys.float_info.max
min_led = None
min_index = 0
for index, led in enumerate(leds):
led_loc = led['location']
dist = distance(loc, led_loc)
if dist < min_dist:
min_dist = dist
min_led = led
min_index = index
return min_dist, min_led, min_index
if __name__ == '__main__':
app.run(host='0.0.0.0', port=2412)
由于 Google Santa Tracker API 一年中只有一天(12 月 24 日)更新,為了測試整個系統,我還編寫了一個模擬真實圣誕老人追蹤器 API 服務器的假圣誕老人追蹤器 API 服務器。有了這個假的 API 服務器,我還可以控制行進速度并根據需要重置。該服務器也是一個 Flask Python 服務器,在 Raspberry Pi 3 的不同端口上運行。
from flask import Flask, request
import json
import time
app = Flask(__name__)
fake_start_time = 0 # initialized to first arrival time from json
real_start_time = 0 # set to start time
speed_factor = 100 # fake clock speed
all_destinations = None
current_info = {
'status': 'OK',
'language': 'en',
'now': None, # Will be set to fake time
'timeOffset': 120000,
'fingerprint': '3b8835bc354c6d5018344b289b833402f7079844',
'refresh': 51449,
'switchOff': False,
'clientSpecific': {
'DisableEarth': False,
'DisableTracker': False,
'DisableWikipedia': False,
'DisablePhotos': False,
'HighResolutionPhotos': False,
'EarthAltitudeMultiplier': 1
},
'routeOffset': 0,
'destinations': None # Will only have destinations up to two towns ahead
}
@app.route('/info')
def info():
if real_start_time != 0:
advance_fake_time()
return app.response_class(json.dumps(current_info), content_type='application/json')
@app.route('/start')
def start():
global real_start_time, speed_factor
real_start_time = real_now()
speed_factor = int(request.args.get('speed', '100'))
print(u'fake clock stated at speed {0}'.format(speed_factor))
return 'ok'
@app.route('/reset')
def reset():
global real_start_time
real_start_time = 0
current_info['destinations'] = all_destinations[:3]
return 'ok'
def index_of_current_destination(ts):
for i, dest in enumerate(all_destinations):
if dest['departure'] > ts:
return i
return 0
def current_destinations():
index = index_of_current_destination(fake_now()) + 3
return all_destinations[:index]
def advance_fake_time():
current_info['now'] = fake_now()
current_info['destinations'] = current_destinations()
def real_now():
return int(time.time() * 100)
def fake_now():
return (real_now() - real_start_time) * speed_factor + fake_start_time
def arrival(d):
return d['arrival']
def load_json():
with open('santa2016.json') as data_file:
data = json.load(data_file)
global all_destinations, fake_start_time
all_destinations = sorted(data['destinations'], key=arrival)
fake_start_time = all_destinations[1]['arrival']
reset()
print(u'{0} destinations loaded, fake_start_time={1}'.format(len(all_destinations), fake_start_time))
if __name__ == '__main__':
load_json()
app.run(host='0.0.0.0', port=1224)
MKR1000 固件
現在 MKR1000 已準備好從 Raspberry Pi 服務器獲取數據,并打開和關閉 LED。
#include
#include
#include
#include "JsonStreamingParser.h"
#include "JsonListener.h"
#define LED_PIN 6
#define LED_NUM 30
#define BRIGHTNESS 50
Adafruit_NeoPixel strip = Adafruit_NeoPixel(LED_NUM, LED_PIN, NEO_GRB + NEO_KHZ800);
char ssid[] = "YOUR_SSID"; // your network SSID (name)
char pass[] = "YOUR_PWRD"; // your network password
int keyIndex = 0; // your network key Index number (needed only for WEP)
int status = WL_IDLE_STATUS;
IPAddress server(192, 168, 1, 120); // numeric IP for RPI server
//char server[] = "rpi3.local"; // name address for RPI server
char endpoint[] = "/santa";
int port = 2412;
// Initialize the Ethernet client library
// with the IP address and port of the server
// that you want to connect to (port 80 is default for HTTP):
WiFiClient client;
class Led {
public:
String name;
int distance;
int presents;
boolean on;
};
Led leds[30];
class LedSwitcher: public JsonListener {
public:
void whitespace(char c) {}
void startDocument() {}
void key(String key) {
Serial.println(key);
currentKey = key;
}
void value(String value) {
Serial.println(value);
if (currentKey == "i") {
ledIndex = value.toInt();
} else if (currentKey == "p") {
presents = value.toInt();
} else if (currentKey == "d") {
distance = value.toInt();
} else {
name = value;
}
}
void endArray() {}
void endObject() {
Serial.println("End of Object");
Serial.print(ledIndex);
Serial.print(":");
Serial.print(name.c_str());
Serial.print(",");
Serial.print(presents);
Serial.print(",");
Serial.print(distance);
leds[ledIndex].on = true;
leds[ledIndex].name = name;
leds[ledIndex].presents = presents;
leds[ledIndex].distance = distance;
}
void endDocument() {}
void startArray() {}
void startObject() {}
int lastLed() {
return ledIndex;
}
private:
String currentKey;
int ledIndex;
int presents;
int distance;
String name;
};
LedSwitcher ledSwitcher;
void connectToWifi() {
// check for the presence of the shield:
if (WiFi.status() == WL_NO_SHIELD) {
Serial.println("WiFi shield not present");
// don't continue:
while (true);
}
// attempt to connect to Wifi network:
while (status != WL_CONNECTED) {
Serial.print("Attempting to connect to SSID: ");
Serial.println(ssid);
// Connect to WPA/WPA2 network. Change this line if using open or WEP network:
status = WiFi.begin(ssid, pass);
// wait 10 seconds for connection:
delay(10000);
}
Serial.println("Connected to wifi");
printWifiStatus();
}
boolean connectToSantaServer() {
Serial.println("Starting connection to server...");
return client.connect(server, port);
}
void ensureConnected() {
if (!client.connected()) {
while (!connectToSantaServer()) {
Serial.println("Failed to connect to server. Retry in 5 seconds");
delay(5000);
}
Serial.println("connected to server");
}
}
void fetchSantaInfo() {
ensureConnected();
// Make a HTTP request:
client.print("GET ");
client.print(endpoint);
client.println(" HTTP/1.1");
client.print("Host: ");
client.println(server);
client.println("Connection: close");
client.println();
int bytes = 0;
boolean isBody = false;
JsonStreamingParser parser;
parser.setListener(&ledSwitcher);
Serial.println();
Serial.println("Received response:");
Serial.println();
while (client.connected()) {
while (client.available()) {
char c = client.read();
++bytes;
//Serial.write(c);
if (isBody || c == '[') {
isBody = true;
parser.parse(c);
}
}
}
Serial.println();
Serial.println();
Serial.println("Disconnecting from server.");
client.stop();
Serial.print("Received: ");
Serial.print(bytes);
Serial.println(" Bytes.");
}
void flashLastLed() {
strip.setPixelColor(ledSwitcher.lastLed(), strip.Color(0, 0, 0));
strip.show();
delay(500);
strip.setPixelColor(ledSwitcher.lastLed(), strip.Color(255, 0, 0));
strip.show();
delay(500);
}
void setup() {
//Initialize serial and wait for port to open:
Serial.begin(9600);
strip.setBrightness(BRIGHTNESS);
strip.begin();
strip.show(); // Initialize all pixels to 'off'.
connectToWifi();
fetchSantaInfo();
for (int i = 0; i < 30; ++i) {
if (leds[i].on) {
strip.setPixelColor(i, strip.Color(255, 0, 0));
} else {
strip.setPixelColor(i, strip.Color(0, 0, 0));
}
}
strip.show();
}
void loop() {
fetchSantaInfo();
delay(1000);
for (int i = 0; i < 10; ++i) {
flashLastLed();
}
}
void printWifiStatus() {
// print the SSID of the network you're attached to:
Serial.print("SSID: ");
Serial.println(WiFi.SSID());
// print your WiFi shield's IP address:
IPAddress ip = WiFi.localIP();
Serial.print("IP Address: ");
Serial.println(ip);
// print the received signal strength:
long rssi = WiFi.RSSI();
Serial.print("signal strength (RSSI):");
Serial.print(rssi);
Serial.println(" dBm");
}
由于大部分工作都是在 Raspberry Pi 上完成的,所以這里的代碼很簡單。它連接到 WiFi,每 10 秒從 Raspberry Pi 獲取圣誕老人數據,并相應地更新 NeoPixels。
現在讓我們啟動它:
結論
MKR1000 是一款非常強大的物聯網應用板,但它也有其自身的局限性。借助更強大的 SBC(單板計算機),如 Raspberry Pi 3,我們將能夠與具有更復雜 API 的任何服務進行交互。
希望你喜歡這個項目作為一個小小的節日驚喜。
- 圣誕老人探測器開源分享
- 基于M5Stack的圣誕老人照相館 0次下載
- Arduino圣誕老人之路
- 使用M5StickC Plus進行M5Stack圣誕節的圣誕老人追蹤器
- Arduino在辦公室地圖上繪制汽車路線
- pcb制作的圣誕老人圣誕別針
- PCB圣誕老人的音序器徽章
- RC Tractor Guy的圣誕老人拖拉機
- 用PCB創建圣誕老人
- 帶領圣誕老人的PCB開發
- 紅色圣誕物聯網卡魯道夫
- 基于LoRa無線射頻信號的行走追蹤系統 8次下載
- 分布式硬件輔助追蹤物聯網測試平臺HATBED 3次下載
- 物聯網是什么?現在物聯網通信標準和協議有哪些?物聯網應用的概述
- 老人GPS定位監護系統相關技術與功能介紹 13次下載
- LED圣誕燈電路圖 LED圣誕燈的工作原理和特點 487次閱讀
- 物聯網的體系架構 989次閱讀
- cps物聯網技術的應用案例 1.3w次閱讀
- 未來物聯網發展的方向有哪些 6198次閱讀
- 如何為物聯網打造神奇的無線世界 919次閱讀
- 如何利用物聯網技術來追蹤藥品 1621次閱讀
- 物聯網的關鍵技術你都掌握了嗎 1337次閱讀
- 6DOF追蹤的進化:從標識追蹤到SLAM定位,帶給AR體驗質的飛越 9625次閱讀
- 物聯網安全隱患有哪些 物聯網安全如何防控 1.7w次閱讀
- 物聯網技術研究與應用案例分析 1.5w次閱讀
- 高精地圖在無人駕駛領域的作用,高精地圖與普通導航地圖的區別 2.2w次閱讀
- 傳感器和物聯網的介紹和“極端物聯網”的詳細資料概述 3805次閱讀
- 農業物聯網的意義_農業物聯網的功能_農業物聯網的應用 9619次閱讀
- 互聯網、物聯網、車聯網這三者有密不可分的關系 并把整個世界趨于一體 2.4w次閱讀
- 應對物聯網安全的五大對策 1137次閱讀
下載排行
本周
- 1山景DSP芯片AP8248A2數據手冊
- 1.06 MB | 532次下載 | 免費
- 2RK3399完整板原理圖(支持平板,盒子VR)
- 3.28 MB | 339次下載 | 免費
- 3TC358743XBG評估板參考手冊
- 1.36 MB | 330次下載 | 免費
- 4DFM軟件使用教程
- 0.84 MB | 295次下載 | 免費
- 5元宇宙深度解析—未來的未來-風口還是泡沫
- 6.40 MB | 227次下載 | 免費
- 6迪文DGUS開發指南
- 31.67 MB | 194次下載 | 免費
- 7元宇宙底層硬件系列報告
- 13.42 MB | 182次下載 | 免費
- 8FP5207XR-G1中文應用手冊
- 1.09 MB | 178次下載 | 免費
本月
- 1OrCAD10.5下載OrCAD10.5中文版軟件
- 0.00 MB | 234315次下載 | 免費
- 2555集成電路應用800例(新編版)
- 0.00 MB | 33566次下載 | 免費
- 3接口電路圖大全
- 未知 | 30323次下載 | 免費
- 4開關電源設計實例指南
- 未知 | 21549次下載 | 免費
- 5電氣工程師手冊免費下載(新編第二版pdf電子書)
- 0.00 MB | 15349次下載 | 免費
- 6數字電路基礎pdf(下載)
- 未知 | 13750次下載 | 免費
- 7電子制作實例集錦 下載
- 未知 | 8113次下載 | 免費
- 8《LED驅動電路設計》 溫德爾著
- 0.00 MB | 6656次下載 | 免費
總榜
- 1matlab軟件下載入口
- 未知 | 935054次下載 | 免費
- 2protel99se軟件下載(可英文版轉中文版)
- 78.1 MB | 537798次下載 | 免費
- 3MATLAB 7.1 下載 (含軟件介紹)
- 未知 | 420027次下載 | 免費
- 4OrCAD10.5下載OrCAD10.5中文版軟件
- 0.00 MB | 234315次下載 | 免費
- 5Altium DXP2002下載入口
- 未知 | 233046次下載 | 免費
- 6電路仿真軟件multisim 10.0免費下載
- 340992 | 191187次下載 | 免費
- 7十天學會AVR單片機與C語言視頻教程 下載
- 158M | 183279次下載 | 免費
- 8proe5.0野火版下載(中文版免費下載)
- 未知 | 138040次下載 | 免費
評論
查看更多