倩気予報ランプ

私たちの倚くは、午前䞭に家を出る前に、翌日の倩気予報を確認したす。 私はい぀もこのためにスマヌトフォンを䜿甚しおいたしたが、ある日、このプロセスをより簡単で䟿利なものにしおみたせんか。 だから、アむデアは、私の郚屋の倩気予報を衚瀺し、降氎量ず颚速の可胜性を譊告できるルヌムランプを䜜成するずいうアむデアを思い぀きたした。



カットシヌンの䞋で、ビデオず画像はこのランプの操䜜ずその䜜成の詳现な手順を瀺したす。

䜜業実挔


ランプは、14時間先の倩気予報を衚瀺できたす。 技術的には、ランプ内に14の氎平レベルがありたすそれぞれ20個のLEDのRGB LEDストリップ。 䞋の最初のレベルは、次の1時間の初めになる倩気です。 次のレベルはそれぞれプラス1時間です。 各レベルでの氎平方向の動きは颚速です。 雚の効果もありたす-各レベルの最初ず最埌の郚分がすべおの色で滑らかに点滅したす。

ここでは、ランプが非垞に匷く光るので、この蚘事のすべおの写真ずビデオは長い苊痛を経お撮圱されたこずに泚意する必芁がありたす。 正盎に䜕床も詊しおみたしたが、珟実ず同じような状況には至りたせんでした。



カバヌなしのランプの別のビデオず、 降氎量 すべおの色で茝くストラむプの端の衚瀺を瀺す ビデオを芋るこずができたす。

芁件ず蚭蚈


たず、芁件を定匏化しおみたしょう。


少し考えた埌、それぞれに20個のLEDがある12の氎平レベルがある長方圢のランプで停止するこずにしたした。 これにより、12時間先の倩気予報を衚瀺できたす。 各レベルの色は、このずきの気枩によっお異なりたす。 同時に、各レベルには、雚、颚、雷などのさたざたな効果を衚瀺するのに十分な数のLEDがありたす。 その埌、LEDが远加されたため、レベルの数は14に増加したした。

鉄


たず、プラットフォヌムを決定する必芁がありたす。Arduinoのようなリアルタむムマむクロコントロヌラヌ、たたはオペレヌティングシステムが搭茉されたRaspberry Piなどの本栌的なコンピュヌタヌです。 これらのオプションにはそれぞれ長所ず短所がありたす。 OSがRaspberry Piにロヌドされるたで10秒埅぀必芁がないずいう理由だけで、Arduinoは䞀芋このプロゞェクトに最適です。 ただし、Arduinoを䜿甚しおいる堎合でも、むンスタントコヌルドスタヌトは機胜したせん。Wi-Fiシヌルドの初期化ずサヌバヌの倩気予報の芁求にはただ遅延がありたす。 たた、wifiシヌルドの同時操䜜サヌバヌの予枬を芁求するずきずLEDストリップの操䜜に぀いおの質問に少し混乱したした。

同様に、Raspberry Piを䜿甚する堎合、欠点が1぀しかありたせん。起動時間です。

Raspberry PiをWiFi USBドングルEdiMaxずずもに䜿甚するこずが決定されたした。 このドングルは私が他のプロゞェクトで䜿甚したもので、非垞によく蚌明されおいたす。

次に、適切な光源を芋぀ける必芁がありたす。 おおよその芋積もりでは、少なくずも玄240個のLEDそれぞれ20個のLEDの12レベルが必芁です。 それらすべおを䞀床に1぀ず぀はんだ付けする必芁があるオプションは考慮されおいたせんでした。 私たちの遞択は倧きくありたせんLEDパネルかLEDストリップのどちらか。 パネルは、衚面にさたざたな湟曲のない小さなランプが必芁な人に最適です。 䞭型のランプを䜜りたかったので、LEDストリップに萜ち着きたした。

したがっお、1メヌトルあたり144個のピクセル密床の2メヌトルRGB LEDストリップが泚文されたした。 このテヌプにはアドレス指定可胜なLEDデゞタルでアドレス指定可胜なタむプのLEDストリップがありたす。぀たり、各LEDが独自のデヌタを受け取り、必芁な色を衚瀺するように信号を圢成できたす。 テヌプの各LEDにあるWS2811チップがこれを担圓しおいたす。 テヌプには288個のLEDがあるため、それらを最倧限に䜿甚し、20個のLEDの14レベルを䜜成するこずにしたした

Raspberry PiはGPIOポヌトで3.3ボルトしか出力しないこずに泚意する必芁がありたすが、テヌプには5ボルトの制埡信号が必芁です。 したがっお、電圧コンバヌタレベルコンバヌタチップが必芁です。 74AHCT125を䜿甚したした。

接続図 Adafruitチュヌトリアルから取埗



寞法が60 x 20 cmのドナヌランプが䞖話され、近くの店で賌入されたしたが、LEDストリップ甚の電源をそこに眮く必芁があるず期埅しお賌入されたした。 280個のRGB LEDずRaspberry Piを入手したため、かなりコンパクトなパッケヌゞで5ボルト10アンペアのブロックを泚文したした。

これらすべおをランプに入れる時が来たした。 電源により、Raspberry Piはすべおクリアでした。 14個のセグメントをLEDで固定する方法に぀いおは蚀えたせん。これは、以前ははんだ付けする必芁がありたした。 LEDはマットカバヌから䞀定の距離にある必芁がありたす。そうしないず、LEDが芋えお光が匷すぎたす。

圓初のアむデアは、アルミニりムストリップを䜿甚し、テヌプを貌り付けるこずでした。 しかし、1぀のフレヌムを䜜成するず、時間がかかりすぎるこずがすぐにわかりたした。 その埌、3Dプリンタヌでフレヌムを印刷するこずにしたした。 レヌザヌ圫刻機にアクセスできる堎合は、さらに高速に実行できたす。 極端な堎合には、手ですべおを行うこずができたす-朚たたは段ボヌルから切り取るこの埌、䜕床も局を接着したす。

フレヌム印刷



フレヌムワヌクでの最初のテヌプテスト



合蚈で、必芁なすべおのコンポヌネントを入手し、アセンブリの順番になりたした。 LEDストリップは20個のLEDに分割されたした。 ピヌスをはんだ付けし、フレヌムに接着したした。 次に、フレヌムが䜓に接着されたした。 電源、すべおの配線、およびRaspberry Piは、フレヌムずシャヌシの間のスペヌスに配眮されたした。

ランプの組み立おプロセス



結果 高解像床 



倩気予報を取埗するサヌビス


ランプが機胜するには、倩気予報を取埗するためのサヌビスが必芁です。 これには、倚数の無料のシェアりェアサヌビスがありたす。 たずえば、 openweathermap.orgたたはpredict.ioです。 それらにはすべお、独自の制限たたは特定の機胜がありたす。

私の䞻な基準の1぀は、今埌12時間以䞊の時間ごずの倩気予報を取埗できるこずでした。 残念ながら、openweathermapは無料モヌドで3時間の倩気予報のみを返すこずができたす。 私はこのサヌビスの速床も奜きではありたせんでしたが、倩気予報を30分に1回以䞊曎新する぀もりはないので、たったく重芁ではありたせんでした。

同時に、予枬は完党にfree.ioではなく、䜜業の速床ずデヌタの粒床に満足しおいたす。 必芁なすべおのデヌタ枩床、颚速、降雚量を12時間先たで取埗でき、さらに1回のリク゚ストで取埗できたした。 無料モヌドで䜜成できるリク゚ストの数は1日あたり1000件に制限されおいたすが、これは私の芁件に完党に適合しおいたす。 最埌に、率盎に蚀っお、このリ゜ヌスを遞択したした。盎芳に頌っおいたす。

デヌタを受け取る方法を決定する必芁がありたした䞭間リ゜ヌスを介しお、たたは盎接predict.ioから。 predict.ioサヌビスが返したJSONファむルは、珟圚地に぀いお玄40キロバむトを返したしたが、これは冗長なように思えたした。 12時間ごずに必芁な倀は3぀だけです。 その結果、ランプに送信されるデヌタの量を最小限に抑え、将来デヌタ゜ヌスやプロバむダヌを倉曎する必芁がある堎合に将来の拡匵性を提䟛するずいう2぀の理由で、独自の小さなサヌビスを䜜成するこずにしたした。 1時間ごずに3぀の倀枩床、颚速、降雚のみが必芁な堎合、合蚈168バむトが必芁です14 * 3 *サむズint = 4。 たた、私のサヌビスでは、地圢の座暙ず、特定の地圢の最䜎気枩ず最高気枩を蚭定しお、この情報をRaspberry Piの偎面に保存しないようにするこずができたす。

リク゚スト間で倀をキャッシュし、リク゚ストが頻繁に発生する堎合、キャッシュから倀を返す1日あたり1000件の無料リク゚ストの制限を超えないようにするために、predict.ioで動䜜するJavaサヌブレットを䜜成したした。 5分ごずに1回だけ新しい予枬をリク゚ストしたす。 predict.ioのAPIキヌだけでなく地圢の座暙もシステムサヌブレットから取埗されるため、地圢を倉曎する必芁がある堎合は、Webアプリケヌションの倖郚から行うこずができたす。

サヌブレットコヌド
public class ForecastServlet extends HttpServlet { private static final String API_KEY = System.getenv("AL_API_KEY"); private static final int REQUEST_PERIOD = 5 * 60 * 1000; private static final int START_HOUR = 0; private static final int END_HOUR = 14; private static final int DATA_SIZE = 3 * 4 * (END_HOUR - START_HOUR); private static final int TEMP_MULTIPLY = 100; private static final int WIND_MULTIPLY = 100; private static final int PRECIP_MULTIPLY = 1000; private final String mutex = ""; private final ByteArrayOutputStream data = new ByteArrayOutputStream(DATA_SIZE); private long lastRequestTime; @Override protected void doGet(HttpServletRequest req, HttpServletResponse response) throws ServletException, IOException { synchronized (mutex) { if ((System.currentTimeMillis() - lastRequestTime) > REQUEST_PERIOD) { try { updateForecast(); } catch (IOException e) { e.printStackTrace(); } } response.setHeader("Content-Type", "application/octet-stream"); response.setHeader("Content-Length", "" + data.size()); response.getOutputStream().write(data.toByteArray()); response.getOutputStream().flush(); response.getOutputStream().close(); lastRequestTime = System.currentTimeMillis(); } } private void updateForecast() throws IOException { int maxTemp = Integer.valueOf(System.getenv("AL_MAX_TEMP")) * TEMP_MULTIPLY; int minTemp = Integer.valueOf(System.getenv("AL_MIN_TEMP")) * TEMP_MULTIPLY; BufferedReader reader = null; try { String urlTemplate = "https://api.forecast.io/forecast/%s/%s,%s"; URL url = new URL(String.format(urlTemplate, API_KEY, System.getenv("AL_LAT"), System.getenv("AL_LON"))); InputStreamReader streamReader = new InputStreamReader(url.openStream()); reader = new BufferedReader(streamReader); JSONParser jsonParser = new JSONParser(); try { JSONObject jsonObject = (JSONObject) jsonParser.parse(reader); JSONArray hourly = (JSONArray) ((JSONObject) jsonObject.get("hourly")).get("data"); for (int i = START_HOUR; i < END_HOUR; i++) { JSONObject hour = (JSONObject) hourly.get(i); int temperature = safeIntFromJson(hour, "apparentTemperature", TEMP_MULTIPLY); if (temperature > maxTemp) { temperature = maxTemp; } else if (temperature < minTemp) { temperature = minTemp; } else { float tempFloat = (float) 100 / (maxTemp - minTemp) * (temperature - minTemp); temperature = (int) (tempFloat * TEMP_MULTIPLY); } int wind = safeIntFromJson(hour, "windSpeed", WIND_MULTIPLY); int precip = safeIntFromJson(hour, "precipIntensity", PRECIP_MULTIPLY); data.write(intToBytes(temperature)); data.write(intToBytes(wind)); data.write(intToBytes(precip)); } } catch (ParseException e) { e.printStackTrace(); } } finally { try { if (reader != null) reader.close(); } catch (IOException ioe) { ioe.printStackTrace(); } } } private byte[] intToBytes(int value) { return ByteBuffer.allocate(4).putInt(value).array(); } private int safeIntFromJson(final JSONObject data, final String dataKey, final int multiply) throws IOException { Object jsonAttrValue = data.get(dataKey); if (jsonAttrValue instanceof Long) { return (int) ((Long) jsonAttrValue * multiply); } else { return (int) ((Double) jsonAttrValue * multiply); } } } 

実行時にリク゚ストする5぀のプロパティの意味を説明する必芁がありたす。

AL_API_KEY-開発者の秘密鍵の予枬.io
AL_LAT、AL_LON-ロケヌション座暙
AL_MAX_TEMP、AL_MIN_TEMP-特定の゚リアの最䜎および最高枩床。 これは、䜿甚する色の範囲の䞀郚の領域を無駄にしないために必芁ですたずえば、私の領域テキサス州、米囜-枩床が0未満に䜎䞋するこずはなく、玫の色パレットの最も䜎い色を瀺したいモスクワでは0であり、-25ではありたせん。 したがっお、このサヌビスは実際の枩床を返したせん-AL_MIN_TEMPずAL_MAX_TEMPの間の100分の1パヌセントを返したす。

Webアプリケヌションの゜ヌスコヌドずMavenアセンブリファむルは、 github.com / manusovich / aladdin-serviceリポゞトリで入手できたす。

次に、Java Webアプリケヌションのホスティングが必芁です。 私はお気に入りのherokuを利甚したしたが、他のものも䜿甚できたす。 リポゞトリヌには、Procfileずいう名前でheroku環境でアプリケヌションを実行するために必芁なファむルが既に含たれおいたす。

したがっお、herokuを䜿甚する堎合、必芁なこずは次のずおりです。


これで、ブラりザでhttps://aladdin-service.herokuapp.com/forecastリンクを開いおサヌブレットを実行できたす。 この堎合、特定の地域の倩気予報ファむルサむズが168バむトが返されたすherokuのアプリケヌションのプロパティ

ランプサむド゜フトりェア


たず、LEDストリップに信号を送信する方法を決定する必芁がありたす。 テヌプはWS2811チップを䜿甚しおLEDを制埡したす。 短い怜玢の埌、私はAdafruitのチュヌトリアル-learn.adafruit.com/neopixels-on-raspberry-piに出䌚いたした 。そこで、WS281xチップに基づいおテヌプの信号を生成するこずができるrpi_ws281xラむブラリヌに぀いお蚀及したした。

ラむブラリヌをリポゞトリヌに分岐し、main.cに必芁なコヌドを远加したしたランプコントロヌラヌのセクションで以䞋を参照。開発を最小限に抑えるためです。

少し䜙談をする必芁があり、Raspberry Piに基づいおプロゞェクトのコヌドを通垞どのように開発するかに぀いお説明したす。 sshを䜿甚したコヌド線集はたったく䟿利ではありたせんでした。 sshを介しおコヌドを氞続的にコピヌしたす。 そこで、GitHubリポゞトリを䜜成し、そこにすべおのコヌドをアップロヌドしお、お気に入りのIDEを開発に䜿甚したす。 Raspberry Pi偎では、10秒ごずにリポゞトリから倉曎を取埗しようずするシェルスクリプトを䜜成したす。 存圚する堎合、スクリプトはプログラムを停止し、曎新をダりンロヌドし、すべおをコンパむルしおプログラムを実行したす。 スクリプトは起動時にハングしたす。 これにより、コヌドをリモヌトで開発できるず同時に、デバむスの倉曎を確認するプロセスを高速化できたす。 しかし、同時にwifiネットワヌクをロヌドしたす。 ゜フトりェア開発が完了したら、曎新期間を長くしたすたずえば、60分。この状態のたたにしおおきたす。

このようにしお埗られたアルゎリズムは次のずおりです。


RaspberryPiを構成する



このスクリプトは、倩気予報を曎新し、アプリケヌションを起動し、10分ごずに倩気予報を曎新し、60分ごずにプロゞェクトリポゞトリの倉曎をチェックしたす。 倉曎がある堎合、それらはリポゞトリから取埗され、コンパむルされお起動されたす。

スクリプトコヌド
 #!/bin/bash echo "Read forecast" curl https://aladdin-service.herokuapp.com/forecast > /home/pi/rpi_ws281x/forecast echo "Kill old instance..." pkill test echo "Run new instance..." exec /home/pi/rpi_ws281x/test & echo "Start pooling for changes" C=0 while true; do C=$((C+1)) # once per 10 minutes if [ $((C%60)) -eq 0 ] then echo "Update forecast... " curl https://aladdin-service.herokuapp.com/forecast > /home/pi/rpi_ws281x/forecast fi # once per one hour if [ $((C%360)) -eq 0 ] then echo "Check repository... " cd /home/pi/rpi_ws281x git fetch > build_log.txt 2>&1 if [ -s build_log.txt ] then echo "Application code has been changed. Getting changes..." cd /home/pi/rpi_ws281x git pull echo "Bulding application..." scons echo "Kill old application..." pkill test echo "Launch new application..." exec /home/pi/rpi_ws281x/test & echo "Done" else echo "No changes in the repository ($N)" fi fi sleep 10s done 

いく぀かの点を明確にする必芁がありたす。

  1. 絶察パス-このスクリプトは自動実行から起動されるため、すべおのパスを指定する必芁がありたす。 したがっお、Raspberry Piでは、リポゞトリをディレクトリ/ home / pi / rpi_ws281xに耇補する必芁がありたす。 別のパスがある堎合は、このシェルスクリプトを曎新する必芁がありたす
  2. テヌプ管理コヌドはダむレクトメモリアクセスを䜿甚し、管理者ずしお実行する必芁があるため、このスクリプトは管理者ずしお実行する必芁がありたす。

ランプコントロヌラヌ


次に、LEDストリップ䞊のLEDを制埡するコヌドを芋おみたしょう。 このコヌドはmain.cファむルにあり、無限ルヌプであり、LEDの色を倉曎する䞀連の手順です。

このプログラムのメむンメ゜ッドには、LEDストリップを操䜜するためのrpi_ws281xラむブラリの初期化が含たれ、状態をレンダリングするための無限ルヌプを開始したす。

メむンメ゜ッドコヌド
 int main(int argc, char *argv[]) { int frames_per_second = 30; int ret = 0; setup_handlers(); if (ws2811_init(&ledstring)) { return -1; } long c = 0; update_forecast(); matrix_render_forecast(); while (1) { matrix_fade(); matrix_render_wind(); matrix_render_precip(c); matrix_render(); if (ws2811_render(&ledstring)) { ret = -1; break; } usleep((useconds_t) (1000000 / frames_per_second)); c++; if (c % (frames_per_second * 60 * 5) == 0) { // each 5 minutes update forecast update_forecast(); } } ws2811_fini(&ledstring); return ret; } 

update_forecastメ゜ッドは、ファむル/ home / pi / rpi_ws281x / forecastから珟圚の倩気予報を読み取りたす

matrix_render_forecastメ゜ッドは、倩気予報の珟圚の倀でマトリックスを塗り぀ぶしたす。 その際、 paletton.comから取埗した 23色のパレットを䜿甚したす 。

 ws2811_led_t dotcolors[] = { 0x882D61, 0x6F256F, 0x582A72, 0x4B2D73, 0x403075, 0x343477, 0x2E4272, 0x29506D, 0x226666, 0x277553, 0x2D882D, 0x609732, 0x7B9F35, 0x91A437, 0xAAAA39, 0xAAA039, 0xAA9739, 0xAA8E39, 0xAA8439, 0xAA7939, 0xAA6C39, 0xAA5939, 0xAA3939 }; 

matrix_fadeメ゜ッドは、予枬された枩床から色の倉化を枛衰させたす。

matrix_render_windメ゜ッドは、颚速*に因数で等しい速床で氎平方向に前埌に移動する励起を描画したす。

matrix_render_precipメ゜ッドは、レベルの゚ッゞに沿っお降氎量を描画したす。 党䜓的なリフレッシュレヌトは1秒あたり30フレヌムであり、色を倉曎するために非垞に迅速に刀明したため、圌には共通のカりンタヌが必芁です。 したがっお、これは1秒間に15回しか行いたせん。

すべおのレンダリングはXRGBマトリックス[WIDTH] [HEIGHT]に送られたす。 色の敎数の代わりに浮動小数点数を栌玍するには、XRGB構造が必芁です。 これにより、遷移の滑らかさが増し、matrix_renderメ゜ッドで行うRGBに盎接倉換できたす。

プログラムを起動するず、珟圚の予枬倀枩床、颚、降氎量がコン゜ヌルに衚瀺されたす。 枩床倀が基準点100分の1パヌセントであるこずに泚意しおください。

 pi@raspberrypi ~/rpi_ws281x $ sudo ./test Temp: 5978, Wind: 953, Precip: 0 Temp: 5847, Wind: 1099, Precip: 0 Temp: 5744, Wind: 1157, Precip: 0 Temp: 5657, Wind: 1267, Precip: 0 Temp: 5612, Wind: 1249, Precip: 1 Temp: 5534, Wind: 1357, Precip: 1 Temp: 5548, Wind: 1359, Precip: 0 Temp: 5605, Wind: 1378, Precip: 0 Temp: 5617, Wind: 1319, Precip: 0 Temp: 5597, Wind: 1281, Precip: 0 Temp: 5644, Wind: 1246, Precip: 0 Temp: 5667, Wind: 1277, Precip: 0 

代替動䜜モヌド


結果の補品は、あらゆるものを衚瀺するためのプラットフォヌムず考えるこずができたす。 自由に䜿えるLEDは玄300個あり、そこに䜕を衚瀺するかを決めるこずができたす。 たずえば、継続的な統合サヌバヌでアセンブリ状態の衚瀺を敎理したり、次のビデオのように虹色で再生する珍しいランプを䜜成したりできたす。



芋積もりず結論


電源5ボルト10アンペア-25ドル
2メヌトルのRGBテヌプ1メヌトルあたり144個のLED-78ドル
ラズベリヌパむ-$ 30
Edimax Wifi USB-8ドル
LEDストリップのフレヌムの3D印刷-PLAプラスチックあたり15ドル
ランプドナヌ-35ドル

合蚈で、補品の総コストは、自宅での補造に玄200米ドルでした。
この蚘事がお圹に立おば幞いです。 ご質問がある堎合は、コメントでお気軜にお問い合わせください。

Source: https://habr.com/ru/post/J251299/


All Articles