16 בפברואר 2024
אגרת לבעלי קופת צדקה - זורע צדקות
13 בפברואר 2024
משלוח מנות PurimIrisBox
תקציר
PurimIrisBox הוא משלוח מנות ייחודי בהשראת סדרת הטלבזיה Stargate, אשר המוטיב המרכזי בו היה IrisDoor המאפשר שער גישה בין נקודות שונות בחלל 🌠
כדי לפתוח את משלוח המנות (או כדי להגיע ל-"אוצר" 😁), ישנם שני מסלולים, כאשר הראשון מבין השניים הוא המומלץ:
הראשון
עברית
English
הוראות הפעלה *
- חבר את כבל הUSB למטען סלולרי / מחשב / סוללה ניידת.
- המתן כ60 שניות להפעלה.
- מצבי הלד ומשמעותם:
- אדום: התוכנה בשלבי טעינה
- צהוב: התוכנה ממתינה לקלט קולי
- ירוק: תשובה נכונה, קופסה פתוחה
השני
פתיחה רגילה של המשלוח - לחיצה על הכפתור - פתרון ל"חלשים"!לקריאה נוספת על משמעות הפתרון
לא להציץ לפני שפותרים! 🙂
תאור הפרוייקט
חיבור מיקרופון הUSB
חיווט מקור מטח חיצוני
חיבור Servo, Led and Switch
רכיבים
Number | Desc | Material | # | |
1 | Cover 2 color | ABS | Black/Green | 1 |
2 | Cover Holder | ABS/PLA | Black | 3 |
3 | Small Cog cover | ABS/PLA | Black | 1 |
4 | Big Cog | PETG | Orange | 1 |
5 | Small Cog | PETG | Orange | 1 |
6 | Fin | ABS | Orange | 5 |
7 | Big Cog Base | ABS | Orange/Black | 1 |
8 | Body | ABS/PLA | Green/White | 1 |
9 | Base Cover | PLA | Black | 1 |
10 | Base Body | PLA | Black | 1 |
11 | USB Holder | PLA | Black | 1 |
12 | Servo Cover | ABS | White | 1 |
12 | Push Button | 1 | ||
13 | PI Zero | 1 | ||
14 | Neo pixel | 1 | ||
15 | Mini Servo SG90 | 1 | ||
16 | USB Type A to micro USB | 1 | ||
17 | Microphone | 1 | ||
18 | Micro USB female | 1 | ||
19 | M3*10mm nut | 6 | ||
20 | BreadBoard wires | 10 | ||
21 | 5V 3 wire junction | 1 | ||
22 | Spacer 2.5mmx0.5cm | 4 | ||
23 | 2.5mm plastic nut | 1 | ||
24 | 3 sec glue | 1 | ||
25 | Epoxy Glue | 1 | ||
26 | Gear lubricant | 1 | ||
27 | DecoArt Sealer/Finisher DAS13 Matte | 1 | ||
28 | Black Marker ProMaker XB Black | 1 | ||
29 | Micro SD Card 32G | 1 |
חיווט
קוד
קרדיט: Rocco's Stuff שממנו קיבלנו השראה לעיצוב. הדיזיין המקורי הופץ תחת רשיון CC BY-NC-SA
עיצוב סכמתי. תוכנה ותכנון אפליקטיבי: אסף מתן
עיצוב פרטני: בועז מתן
סיכום
כמו בפרוייקטים הקודמים, גם כאן, מה שמנחה אותי הוא השילוב בין רוחניות וטכנולוגיה.
מי שדם דומה זורם בעורקיו, מוזמן ליצור קשר asaf.matan@gmail.com או בתגובות למטה.
רשיון
CC (Creative Commons): This is the organization that offers various standardized licenses to enable content creators to share their work with specific conditions.
BY (Attribution): This element requires that the original creator must be credited whenever the work is used or shared. The user must acknowledge the authorship in a manner specified by the creator or licensor.
NC (Non-Commercial): The work can be used and shared, but only for non-commercial purposes. This means it cannot be used in ways that are primarily intended for or directed toward commercial advantage or monetary compensation.
ND (No Derivatives): The work can be shared, but it must remain unchanged and whole, meaning no derivatives or adaptations of the work are allowed. This means you cannot edit, alter, or build upon the work in any way.
9 בדצמבר 2023
קופת צדקה זורע צדקות - Charity Sower
מבוא
הביטוי "זורע צדקות"
"זוֹרֵעַ צְדָקוֹת. מַצְמִיחַ יְשׁוּעוֹת" (סידור התפילה).
"וילבש צדקה כשריון וכובע ישועה בראשו"(ישעיה נ"ט).
מסביר האדמו"ר הזקן - "ודרשו רז"ל מה שריון זה כל קליפה וקליפה מצטרפת לשריון גדול אף צדקה כל פרוטה ופרוטה מצטרפת לחשבון גדול".
תאי אחסון
אופן השימוש
תמונות נוספות
מילות סיכום
רשיון
CC (Creative Commons): This is the organization that offers various standardized licenses to enable content creators to share their work with specific conditions.
BY (Attribution): This element requires that the original creator must be credited whenever the work is used or shared. The user must acknowledge the authorship in a manner specified by the creator or licensor.
NC (Non-Commercial): The work can be used and shared, but only for non-commercial purposes. This means it cannot be used in ways that are primarily intended for or directed toward commercial advantage or monetary compensation.
ND (No Derivatives): The work can be shared, but it must remain unchanged and whole, meaning no derivatives or adaptations of the work are allowed. This means you cannot edit, alter, or build upon the work in any way.
20 באפריל 2023
"זורע צדקות" - "Charity Sower" - אביב 2023
תקציר
"זוֹרֵעַ צְדָקוֹת. מַצְמִיחַ יְשׁוּעוֹת" (סידור התפילה).
"וילבש צדקה כשריון וכובע ישועה בראשו"(ישעיה נ"ט).
מסביר האדמו"ר הזקן - "ודרשו רז"ל מה שריון זה כל קליפה וקליפה מצטרפת לשריון גדול אף צדקה כל פרוטה ופרוטה מצטרפת לחשבון גדול".
תאור הפרוייקט
רכיבים
- Seeed Wio Terminal Main controller
- Seeed GPS Module Air 530
- חיישן מרחק VCNL4040 של חברת Adafruit
- כבל USB Type A- TypeC עם מחבר זוויתי
הדפסות תלת מימד והרכבה
חומרה, חיווט ותוכנה
חיווט חשמלי
קוד המקור
// Written by Asaf Matan | |
// GPS code is based on | |
// https://www.hackster.io/SeeedStudio/wio-terminal-gps-ad70e2 Arduino/Wappsto | |
// example | |
#include <Adafruit_VCNL4040.h> | |
#include <SPI.h> | |
#include "TFT_eSPI.h" | |
#include <Seeed_FS.h> | |
#include <SoftwareSerial.h> | |
#include <TinyGPS++.h> | |
#include <WiFiClientSecure.h> | |
#include <WiFiMulti.h> | |
#include "Free_Fonts.h" | |
#include "RawImage.h" | |
#include "SD/Seeed_SD.h" | |
#include "Wappsto.h" | |
#include "WappstoValueTypes.h" | |
#include "wappsto_config.h" | |
#include "my_secrets.h" | |
WiFiMulti wifiMulti; | |
WiFiClientSecure client; | |
Wappsto wappsto(&client); | |
#define BUZZER_PIN WIO_BUZZER | |
Network *myNetwork; | |
Device *myDevice; | |
Value *myLatitudeValue; | |
Value *myLongitudeValue; | |
DeviceDescription_t myDeviceDescription = { | |
.name = "Location Device1", | |
.product = "", | |
.manufacturer = "", | |
.description = | |
"Example sending location shown on map in the Wappsto dashboard", | |
.version = "1.0", | |
.serial = "00001", | |
.protocol = "Json-RPC", | |
.communication = "WiFi", | |
}; | |
double myLatitude = MY_LAT; | |
double myLongitude = MY_LON; | |
void updateLocation(double valLatitude, double valLongitude) { | |
myLatitudeValue->report(valLatitude); | |
myLongitudeValue->report(valLongitude); | |
} | |
void refreshLocationCallback(Value *value) { | |
// Nothing to read from wappsto no nothing to refresh | |
} | |
bool skip_wifi = false; | |
const char *ssid = MY_SSID; | |
const char *password = MY_PASS; | |
Adafruit_VCNL4040 vcnl4040 = Adafruit_VCNL4040(); | |
SoftwareSerial mySerial(0, 1); // RX, TX | |
TinyGPSPlus gps; | |
TFT_eSPI tft; | |
TinyGPSCustom ExtLat(gps, "GPGGA", 3); // N for Latitude | |
TinyGPSCustom ExtLng(gps, "GPGGA", 5); // E for Longitude | |
const float pi = 3.1415; | |
int menu = 0, p_menu = 3; | |
int logging = 0, sat_n = 0; | |
int coin_counter = 0; | |
int wappsto_transmited_lines = 0; | |
String F_name = "db.txt"; | |
String p_hour, p_lat, p_lng, p_alt, p_sat, p_date; | |
String p_coin_counter; | |
// for Satellites position | |
static const int MAX_SATELLITES = 40; | |
TinyGPSCustom totalGPGSVMessages(gps, "GPGSV", | |
1); // $GPGSV sentence, first element | |
TinyGPSCustom messageNumber(gps, "GPGSV", | |
2); // $GPGSV sentence, second element | |
TinyGPSCustom satsInView(gps, "GPGSV", 3); // $GPGSV sentence, third element | |
TinyGPSCustom satNumber[4]; // to be initialized later | |
TinyGPSCustom elevation[4]; | |
TinyGPSCustom azimuth[4]; | |
TinyGPSCustom snr[4]; | |
struct { | |
bool active; | |
int elevation; | |
int azimuth; | |
int snr; | |
int dsp; | |
} sats[MAX_SATELLITES]; | |
String N_date, hour0; | |
bool sd; | |
String lat0, lng0; | |
void disp_title() { | |
if (menu == 0) { | |
tft.setFreeFont(FF0); | |
tft.fillScreen(TFT_BLACK); | |
tft.setTextSize(3); | |
tft.drawString("GPS", 120, 3); | |
tft.setTextSize(2); | |
tft.drawString("Date", 30, 42); | |
tft.drawString("Time", 30, 74); | |
tft.drawString("LAT", 30, 106); | |
tft.drawString("LONG", 30, 138); | |
tft.drawString("ALT", 30, 170); | |
tft.drawString("Satellites", 30, 202); | |
if (sd != true) { | |
tft.drawChar(295, 223, 'S', TFT_WHITE, TFT_RED, 2); | |
tft.drawChar(307, 223, 'D', TFT_WHITE, TFT_RED, 2); | |
} | |
p_hour = " "; | |
p_lat = " "; | |
p_lng = " "; | |
p_alt = " "; | |
p_sat = " "; | |
p_date = " "; | |
} else if (menu == 1) { | |
tft.setFreeFont(FF0); | |
tft.fillScreen(TFT_BLACK); | |
tft.setTextSize(3); | |
tft.drawString("Stats", 120, 3); | |
tft.setTextSize(2); | |
tft.drawString("Coins", 30, 42); | |
p_coin_counter = " "; | |
} | |
} | |
String doubleToString(double value, int precision) { | |
char buffer[20]; | |
dtostrf(value, 0, precision, buffer); | |
return String(buffer); | |
} | |
void displayInfo() { | |
if (gps.location.isValid()) { | |
lat0 = doubleToString(gps.location.lat(), 6); | |
lng0 = doubleToString(gps.location.lng(), 6); | |
} else { | |
lat0 = "0"; | |
lng0 = "0"; | |
} | |
if (gps.date.isValid()) { | |
N_date = String(gps.date.day()) + "/" + String(gps.date.month()) + "/" + | |
String(gps.date.year()); | |
} else { | |
N_date = "00/00/0000"; | |
} | |
if (gps.time.isValid()) { | |
hour0 = ""; | |
if (gps.time.hour() < 10) hour0 = "0"; | |
hour0 += gps.time.hour(); | |
hour0 += ":"; | |
if (gps.time.minute() < 10) hour0 += "0"; | |
hour0 += gps.time.minute(); | |
hour0 += ":"; | |
if (gps.time.second() < 10) hour0 += "0"; | |
hour0 += gps.time.second(); | |
} else { | |
hour0 = "00:00:00"; | |
} | |
} | |
void displayInfoData(void) { | |
if (menu == 0) { // menu 0 | |
if (menu != p_menu) { | |
disp_title(); | |
p_menu = menu; | |
} | |
if (N_date != p_date) { // Date | |
tft.fillRect(100, 42, 120, 16, TFT_BLACK); | |
tft.drawString(N_date, 100, 42); | |
p_date = N_date; | |
} | |
if (hour0 != p_hour) { // Time | |
tft.fillRect(100, 74, 120, 16, TFT_BLACK); | |
tft.drawString(hour0, 100, 74); | |
p_hour = hour0; | |
} | |
if (lat0 != p_lat) { // Latitude | |
tft.fillRect(100, 106, 132, 16, TFT_BLACK); | |
tft.drawString(lat0, 100, 106); | |
p_lat = lat0; | |
} | |
if (lng0 != p_lng) { // Longitude | |
tft.fillRect(100, 138, 156, 16, TFT_BLACK); | |
tft.drawString(lng0, 100, 138); | |
p_lng = lng0; | |
} | |
if (String(gps.altitude.meters()) != p_alt) { // Altimeter | |
tft.fillRect(100, 170, 60, 16, TFT_BLACK); | |
tft.drawString(String(gps.altitude.meters()), 100, 170); | |
p_alt = String(gps.altitude.meters()); | |
} | |
if (String(gps.satellites.value()) != p_sat) { // N of Satellites | |
tft.fillRect(160, 202, 32, 16, TFT_BLACK); | |
tft.drawString(String(gps.satellites.value()), 160, 202); | |
p_sat = String(gps.satellites.value()); | |
} | |
} // end of menu=0 | |
else if (menu == 1) { | |
if (menu != p_menu) { | |
disp_title(); | |
p_menu = menu; | |
} | |
if (String(coin_counter) != p_coin_counter) { // Coin Counter | |
tft.fillRect(100, 42, 120, 16, TFT_BLACK); | |
tft.drawString(String(coin_counter), 100, 42); | |
p_coin_counter = String(coin_counter); | |
} | |
} | |
} | |
void playTone(int tone, int duration) { | |
for (long i = 0; i < duration * 1000L; i += tone * 2) { | |
digitalWrite(BUZZER_PIN, HIGH); | |
delayMicroseconds(tone); | |
digitalWrite(BUZZER_PIN, LOW); | |
delayMicroseconds(tone); | |
} | |
} | |
void writeIntToSD(const char *filename, int value) { | |
File file = SD.open(filename, FILE_WRITE); | |
if (file) { | |
file.print(value); | |
file.close(); | |
} else { | |
Serial.println("Error writing to file"); | |
} | |
} | |
int readIntFromSD(const char *filename) { | |
File file = SD.open(filename, FILE_READ); | |
int value = 0; | |
if (file) { | |
value = file.parseInt(); | |
file.close(); | |
} else { | |
Serial.println("Error reading from file"); | |
} | |
return value; | |
} | |
String *parseCSV(String data, int &fieldCount) { | |
String *fields = | |
new String[data.length()]; // Allocate memory for the maximum | |
// expected number of fields | |
fieldCount = 0; | |
// Split the data string into fields | |
int lastIndex = 0; | |
int commaIndex = data.indexOf(','); | |
while (commaIndex != -1) { | |
fields[fieldCount] = data.substring(lastIndex, commaIndex); | |
fieldCount++; | |
lastIndex = commaIndex + 1; | |
commaIndex = data.indexOf(',', lastIndex); | |
} | |
fields[fieldCount] = data.substring(lastIndex); | |
fieldCount++; | |
return fields; | |
} | |
void initializeWifi(void) { | |
Serial.println("Initializing WiFi"); | |
wifiMulti.addAP(ssid, password); | |
while (wifiMulti.run() != WL_CONNECTED) { | |
Serial.print("."); | |
delay(500); | |
} | |
Serial.print("IP address: "); | |
Serial.println(WiFi.localIP()); | |
} | |
void proximity_handler(void) { | |
int proximity_value; | |
proximity_value = vcnl4040.getProximity(); | |
if (proximity_value > 50) { | |
if (sd == true) { | |
Serial.println(F_name); | |
File myFile = SD.open(F_name, FILE_APPEND); | |
if (myFile) { | |
myFile.print(N_date); | |
myFile.print(","); | |
myFile.print(hour0); | |
myFile.print(","); | |
myFile.print(lat0); | |
myFile.print(","); | |
myFile.print(lng0); | |
myFile.print(","); | |
myFile.println(gps.altitude.meters()); | |
myFile.close(); | |
} | |
} | |
Serial.println("Proximity Value:"); | |
Serial.println(proximity_value); | |
coin_counter += 1; | |
// Store Coin Counter to SD | |
writeIntToSD("coin_counter.txt", coin_counter); | |
delay(1500); | |
} | |
} | |
void wappsto_transmit(void) { | |
if (digitalRead(WIO_KEY_B) == LOW) { | |
int line_counter = 0; | |
// Log to SD card | |
if (sd == true) { | |
File myFile = SD.open(F_name, FILE_READ); | |
if (myFile) { | |
while (myFile.available()) { | |
int fieldCount; | |
String line = myFile.readStringUntil('\n'); | |
if (line.length() > 0) { | |
Serial.println(line); | |
String *fields = parseCSV(line, fieldCount); | |
// Print the fields | |
for (int i = 0; i < fieldCount; i++) { | |
Serial.print("Field "); | |
Serial.print(i); | |
Serial.print(": "); | |
Serial.println(fields[i]); | |
if (i == 2) { | |
myLatitude = fields[i].toDouble(); | |
} else if (i == 3) { | |
myLongitude = fields[i].toDouble(); | |
} | |
} | |
delete[] fields; // Deallocate the memory used for the fields array | |
if (line_counter >= wappsto_transmited_lines) { | |
if (myLatitude > 0) { | |
Serial.print("Transmited line: "); | |
Serial.println(line_counter); | |
updateLocation(myLatitude, myLongitude); | |
wappsto.dataAvailable(); | |
} | |
wappsto_transmited_lines++; | |
} | |
line_counter++; | |
} | |
} | |
myFile.close(); | |
Serial.print("Updating transmitted lines to: "); | |
Serial.println(wappsto_transmited_lines); | |
writeIntToSD("wappsto_transmitted_lines.txt", wappsto_transmited_lines); | |
} else { | |
Serial.println("Failed to open file"); | |
} | |
while (digitalRead(WIO_KEY_B) == LOW) { | |
} | |
} | |
} | |
} | |
void setup() { | |
Serial.begin(57600); | |
pinMode(WIO_KEY_A, INPUT_PULLUP); | |
pinMode(WIO_KEY_B, INPUT_PULLUP); | |
pinMode(WIO_KEY_C, INPUT_PULLUP); | |
pinMode(WIO_5S_PRESS, INPUT_PULLUP); | |
pinMode(BUZZER_PIN, OUTPUT); | |
if (digitalRead(WIO_5S_PRESS) == HIGH) { | |
mySerial.begin(9600); | |
} | |
// Let things stablize (better than !Serial for this case) | |
delay(2000); | |
tft.begin(); | |
tft.setRotation(3); | |
Serial.print("Initializing SD card..."); | |
if (!SD.begin(SDCARD_SS_PIN, SDCARD_SPI)) { | |
Serial.println("initialization failed!"); | |
sd = false; | |
tft.drawChar(295, 223, 'S', TFT_WHITE, TFT_RED, 2); | |
tft.drawChar(307, 223, 'D', TFT_WHITE, TFT_RED, 2); | |
} else { | |
Serial.println("initialization done."); | |
sd = true; | |
} | |
// Display splash screen | |
tft.setFreeFont(&FreeSerifBoldItalic12pt7b); | |
tft.fillScreen(TFT_WHITE); | |
tft.setTextColor(TFT_RED); | |
tft.drawString("Charity Sower", 90, 10); | |
drawImage<uint16_t>("coin.bmp", 100, 50); | |
delay(5000); | |
tft.fillScreen(TFT_BLACK); // Black background | |
tft.setTextColor(TFT_YELLOW); | |
// read the stored integer from the SD card | |
coin_counter = readIntFromSD("coin_counter.txt"); | |
// for satellites position | |
// Initialize all the uninitialized TinyGPSCustom objects | |
for (int i = 0; i < 4; ++i) { | |
satNumber[i].begin(gps, "GPGSV", 4 + 4 * i); // offsets 4, 8, 12, 16 | |
elevation[i].begin(gps, "GPGSV", 5 + 4 * i); // offsets 5, 9, 13, 17 | |
azimuth[i].begin(gps, "GPGSV", 6 + 4 * i); // offsets 6, 10, 14, 18 | |
snr[i].begin(gps, "GPGSV", 7 + 4 * i); // offsets 7, 11, 15, 19 | |
} | |
if (!vcnl4040.begin()) { | |
Serial.println("Couldn't find VCNL4040 chip"); | |
while (1) | |
; | |
} | |
Serial.println("Found VCNL4040 chip"); | |
if (digitalRead(WIO_5S_PRESS) == HIGH) { | |
Serial.println("Skip Wifi Setup"); | |
skip_wifi = true; | |
} else { | |
initializeWifi(); | |
initializeNtp(); | |
wappsto.config(network_uuid, ca, client_crt, client_key, 5, NO_LOGS); | |
if (wappsto.connect()) { | |
Serial.println("Connected to Wappsto"); | |
} else { | |
Serial.println("Could not connect"); | |
} | |
Serial.println("Create network"); | |
myNetwork = wappsto.createNetwork("Location Example"); | |
Serial.println("Create Device"); | |
myDevice = myNetwork->createDevice(&myDeviceDescription); | |
Serial.println("Create Default val"); | |
myLatitudeValue = myDevice->createNumberValue(&defaultLatitudeParameter); | |
myLongitudeValue = myDevice->createNumberValue(&defaultLongitudeParameter); | |
Serial.println("Register callbacks"); | |
myLatitudeValue->onRefresh(&refreshLocationCallback); | |
myLongitudeValue->onRefresh(&refreshLocationCallback); | |
Serial.println("read last entry transmitted to wappsto"); | |
wappsto_transmited_lines = readIntFromSD("wappsto_transmitted_lines.txt"); | |
Serial.print("Last transmitted line:"); | |
Serial.println(wappsto_transmited_lines); | |
} | |
} | |
void loop() { | |
if (skip_wifi == false) { | |
Serial.println('.'); | |
wappsto.dataAvailable(); | |
delay(500); | |
} | |
while (mySerial.available() > 0) { | |
char c = mySerial.read(); | |
// Serial.print(c); | |
gps.encode(c); | |
} | |
displayInfo(); | |
displayInfoData(); | |
proximity_handler(); | |
// Screen toggle | |
if (digitalRead(WIO_KEY_C) == LOW) { // Page(menu) change | |
menu++; | |
if (menu > 1) menu = 0; | |
while (digitalRead(WIO_KEY_C) == LOW) { | |
} | |
} | |
if (coin_counter > 10) { | |
playTone(500, 1000); | |
coin_counter = 0; | |
} | |
// Handle transmitting of stored coordinates to wappstop incase we are in wifi | |
// on mode. | |
wappsto_transmit(); | |
} | |