|
Arduino Nano IoT 33Arduino
|
x 1 | |
|
RGB LED |
x 4 | |
|
1.3 Inch Oled Display Module |
x 1 | |
|
HC-SR04 Ultrasonic Ranging Sensor Module |
x 1 | |
|
Momentary Switch, Red |
x 1 |
|
Autodesk Fusion 360Autodesk
|
|
|
EagleAutodesk
|
IoT Smart Desk Distance and KPI Meter
HTTPS://WWW.RAISINGAWESOME.SITE
INTRODUCTION
This IoT project was born out of the uncertain times presented by COVID-19. Across the world, many teams that normally were centralized daily to a work location suddenly found themselves working remotely. The first to be displaced to work remotely from their homes were due to child care facilities being closed. For all, a concern over still being a visible, productive part of the team began to overwhelm their thoughts. So, this project set out to use an Arduino Nano 33 IoT to keep the team in one another's thoughts, provide a kit project for the kiddies and parents to work together, provide a 6 foot social distancing sensor, and ensure critical business processes stayed in compliance.
The end product is a desk ornament to provide the following functions:
- Family Fun: Provide a kit for employees and their kids to learn about engineering, design, and the Arduino.
- Remote Team Comradery: Have a button that when pressed, it will communicate via IoT to all other kits and flash their LEDs. Employees are to do this anytime they do something awesome to let the remote team know.
- Social Distancing Stewardship: Provide a 6 Foot Social Distance sensor on one's desk. A proximity sensor will flash the LEDs on the module if someone is within 6 feet.
- Business Compliance: Alert an employee if critical business compliance metrics are potentially in jeopardy (due to overdue action items, training assignments, etc).
- Display the Joke of the Day from a Web API
- Display a Clock from a Web API Time Server
- Display the company Stock Price from the Yahoo Stocks API
The key business rules are for Health, Safety, and Environmental work processes, Process Safety Management work processes such as Management of Change compliance, and Mechanical Integrity work processes such as turnaround planning.
PRODUCT DESIGN
First, a note on the visual design. I work for a petroleum refining company. This project is a home hobby project, but is a present to my team's families - so, in the renderings, I changed my company logos out for the contest. For my team, the gas pumps make more sense. The ARC logo is in reference to the web application suite I developed to provide digital workflow and competency development for the various teams. For this gizmo, it enables the data mining for the IoT using Microsoft Azure based products.
I drew inspiration from this awesomely restored vintage gas station in McClean, Texas:
Trip Advisor Picture of Restored Gas Station on Route 66
As my usual M.O., I used Autodesk Fusion 360 to create the product in 3D to ensure everything would fit. To be sure it is not frustrating to a kid to assemble, I made sure everything would simply push together - no need for glue. If you haven't ever used Autodesk Fusion 360, you must get it. It's free and you can design anything including woodworking and buildings. It's a life changer for a maker and engineer.
CIRCUIT DESIGN
The circuit is an expansion on the classing Blink circuit. RGB LEDs are simply 3 LEDs in one bulb. There is a lead for voltage on each LED and a shared ground. So, there is a resistor for each voltage lead. The choice of resistor depends on your specs of LED. You can use an online calculator2 to limit your amps accordingly per your LED specs. I'm using 100 Ohm.
Arduino Circuit - Controls LEDs, OLED Display, and the Proximity Sensor
Note, there is a voltage divider on the echo pin (Distance-3 pin) coming back from the proximity sensor. This is because it is echoing 5V's, but the Nano has 3.3V operating logic voltage. A "voltage divider" is a simple circuit that lets you tap the voltage between two resistors. I created an Alexa Skill3 that will help you determine the resistors you need, but in this case 3.3K and 1.7K do the trick from 5V to 3.3V.
The circuit also communicates to an OLED display on the gas pump using I2C. This will be used to give specifics if an LED is ever yellow or red signifying a compliance issues - or stating that someone has breached the 6 foot Social Distancing rule.
PCBWay Board LAYOUT
To ensure it would fit into my ornament, I pushed it from Eagle to Autodesk Fusion 360. I found that I needed to seek some 3D packages, though, but this served great for an obstruction model:
Autodesk Fusion 360 Push from Eagle made for a good "obstruction" model
IoT Strategy
The kicker to this project is to have the LEDs change based on a set of business rules. The easy business rule is to stay 6 feet away from one's desk for social distancing stewardship. The hard one is having my ARC framework chew up data from all kinds of digital systems to determine when compliance is in the yellow or in the red such as when non-discretionary action items are overdue from incident investigations. Also, when a button is pressed on the oil reservoir station on the right, we want to flash all other units that the other team members have on their desks.
To pull this off, we'll use web sockets - something the Nano will handle nicely. Each device will simply act as a web client and hit the "mothership" web application with a querystring every minute.
For example: http:///mymothership.com/myapp.aspx?ID=username&Action=GetStatus
The mothership app will do some IF...Thens to return back what to do with the LEDs on the calling device. No need for JSON or anything fancy - just 5 lines of text that state the following:
OLED scroll - what to show on the top line of the OLED
LED 1 Color
LED 2 Color
LED 3 Color
Unit Status
The "Unit Status" line can override the rest if certain commands are sent such as "celebrate". This will occur when a user presses the button on the oil reservoir (shown on the right). The lights will do a pre-canned flash sequence for fun to boost team comradery.
TEST SERVER SETUP
As discussed in the IOT Strategy section, our desk ornament will be a web client that hits a web server to get a data packet. The packet will provide instruction on what it should do with its OLED and LEDs. So, we need a web server to play around with this.
If you have a Windows PC with any operating system since the mid 90's, you already have a web server! You just need to enable it.
To do so, just type "Turn Windows Features On or Off" in the search bar. Then put a check by Internet Information Services. Type http://localhost in your browser and you should see the IIS Start page. Bam! - you now have your own web server! You can even open a port on your firewall and hit it from anywhere in the world that has internet access.
Store the following script as c:\inetpub\wwwroot\default.aspx with this one and you'll have a good mothership IoT server to play with. Scroll down below for the code in the independent Code Section.
CLIENT CODE
Even though we just have 4 LEDs, an OLED display, and a switch, it takes a lot of code to get this one done. I used an interrupt on pin 13 to catch the button press. Then some If..thening sends a message to the mother ship server application and lights things up accordingly.
You can find this code in the Code Section below.
BUILD PHOTOS
BEFORE ORDERING MY PCB, I PERFECTED THE CIRCUIT BY BREADBOARDING
My first Eagle circuit design was actually way off. I didn't consider future use of PWM to have dimming effects and I forgot the button. So, once again, breadboarding saves the day. I'd been pretty upset after waiting two weeks to get a PCB that requires additional jumpers.
TO ELIMINATE THE WICKER LOOK OF 3D PRINTING,
I USE SQUADRON PRODUCTS WHITE PUTTY
You can set a 3D printer to print very fine to minimize the wicker look it has from its process. However, it still never looks perfect. For ABS plastic, you can use cold acetone vapor polishing4 to smooth out the surface, but its tricky and you can over do it. For this build, we are using PLA, so we are using putty instead. I came across Squadron Products putty almost 10 years ago when we built our R2D2. I used it to fill in cracks on High Impact Polystyrene, before 3D printing was affordable. It stood the test of time and never became brittle or fell out over the years. So, I purchased some new for this project. The formula seems to have changed to make it super fast drying to allow quick handling.
MY INDOOR MAKESHIFT PAINTBOOTH
My wife and I first painted with acrylic to give an even color base since some parts where printed in silver, some black, and some red. I then took it to my basement paint booth. It's a cardboard box with TIG rod hangers that I can spin around from the top allowing to spray all sides. I point a fan on it after so I can coat every 20 minutes.
Since this is for my work buds, I kept my employer's logos off for this competition and gave a nod to the community, but I'm happy with the results. I love how Autodesk Fusion 360 will let you visualize every joint and minute detail. Clicking the button on top, it's really cool how fast the Arduino Nano IoT 33 hits the server and parses the response. A+ !!
PROJECT SUMMARY
This was a great project to showcase the Arduino Nano IoT 33. With so many PWM/Analog/Digital pins available, we needed very few parts. It's built-in WiFi allowed us to easily connect to our own custom web page. In turn, we have a great desk ornament gift for the members of my team with some cool smarts built in to help them stay on top of their critical work. Last, the proximity sensor in the ornament will take what is an awkward moment into a fun moment when office visitors recognize they are too close in terms of social distancing in response to COVID-19.
First Prototype
As a side benefit, if you never set up your own web server, I hope you are inspired to do so. It's just a few clicks and has been for over 20 years! But, so few even know they have that capability. With it, you can build an IoT "cloud" hub of your very own - free of charge.
For more projects and build videos, check out our past blogs covering everything from automotive products to 4D Game Engines.
Stay Safe,
-Sean and Connor
HTTPS://WWW.RAISINGAWESOME.SITE
// ARC Paperweight Code
// Copyright 2021 by Sean J. Miller
#include "arduino_secrets.h" //holds all Global Variables to declutter this code
#define DEBUG true
void setup() {
if (DEBUG) Serial.begin(9600);
Serial.begin(9600);
rtc.begin(); // initialize RTC
timer_setup();//Sets up a timer to callback every so many milliseconds. Used for threading if needed.
setupPins();//Sets all the pins to the ARC_Ornament LED needs.
delay(2000);//Allow time for the OLED display to kick in.
setupOLED();
LED1="OFF";LED2="OFF";LED3="YELLOW";
setTheLightsPerTheVariables();
//online=GetUserWiFi();//this will wait until an adhoc connection is made and the user gives their local WiFi SSID and Password via their phone.
online=true; cls();
showOLEDMessage("Syncing w/Cloud",4);
if (online) {
GetStockPriceFromYahoo();
//GetJokeOfTheDayAPI();
GetTime();//Gets the time from the WorldTimeAPI.org server for central time. Need to add an option to select the timezone by one's phone.
}
cls();
LED1="OFF";LED2="OFF";LED3="OFF";
setTheLightsPerTheVariables();
}
void loop() {
readDistanceAndAlert();
if (online) {
//showJoke(); //jokes were too inappropriate for work
showStock();
}
showCoreValues();
if (DEBUG) Serial.println(the_time);
for (int ii=0;ii<200;ii++)
{ if (online) showTime(); else showOLEDMessage(" Be Safe",3);
if (ii%10==0){
readDistanceAndAlert();//sets the distance variable and does the checks for action
showDistanceMessage();// displays the distance to the OLED
}
}
}
void GetStock()
{
if ((millis()-millis_when_stock_set>(60*1000*15))||millis()<millis_when_stock_set) GetStockPriceFromYahoo();
}
void GetTime()
{
bool checker=false;
cls;
showOLEDMessage("Getting time...",4);
int ii=0;
while (!checker) {
checker=GetTimeFromWorldTimeAPI();
if (ii++>4) {
break;
}
}
}
void showStock()
{
if (the_stock!="0")
{
cls();
String the_temp=" "+the_stock;
showOLEDMessage(" PSX Stock:",1);
showOLEDMessage(&the_temp[0],3);
delay(5000);
cls();
}
}
void showTime()
{ UpdateTheTimeDisplayText();// Builds the display of time from the millis() passed since the time was set by the WorldTimeAPI.
showOLEDMessage(&the_time[0],6);
showOLEDMessage(&the_date[0],7);
}
void showJoke()
{
if (millis()-joke_time_retrieved>(1000*60*60*4)) GetJokeOfTheDayAPI();
cls();
String the_temp;
the_joke.replace("\\r\\n"," ");
the_joke.replace("\\\"",";");
the_temp=" Joke of the Day: " + the_joke + " ";
for (int ii=0;ii<(the_temp.length()-1);ii++) {
showOLEDMessage(&the_temp[ii],3);
readDistanceAndAlert();
delay(100);
}
}
void showCoreValues()
{
the_joke="Safety - Honor - Committment";
cls();
String the_temp;
the_joke.replace("\\r\\n"," ");
the_temp=" Core Values: " + the_joke + " ";
for (int ii=0;ii<(the_temp.length()-1);ii++) {
showOLEDMessage(&the_temp[ii],3);
readDistanceAndAlert();
delay(100);
}
}
void UpdateTheTimeDisplayText(){
//Since the RTC of the Nano drifts rather quickly, update the time from the cloud every 1/2hr
if (((millis()-millis_when_time_set)>(60*1000*60*.5)||millis()<previous_millis)&&online) GetTime();
String show_second="0" + String(rtc.getSeconds());
show_second=show_second.substring(show_second.length()-2);
String show_minute="0" + String(rtc.getMinutes());
show_minute=show_minute.substring(show_minute.length()-2);
String show_hour="0" + String(rtc.getHours());
show_hour=show_hour.substring(show_hour.length()-2);
String show_day="0" + String(rtc.getDay());
show_day=show_day.substring(show_day.length()-2);
String show_month="0" + String(rtc.getMonth());
show_month=show_month.substring(show_month.length()-2);
String show_year= "20" + String(rtc.getYear());
previous_millis=millis();
the_time = show_hour + ":" + show_minute + ":" + show_second;
the_date = " " + show_month +"/" + show_day + "/" + show_year;
}
void buttonPressed()
{//Called by the interrupt (ISR) when the button is pressed.
if (button_pressed) return;
detachInterrupt(digitalPinToInterrupt(13));
button_pressed=true;
if (DEBUG) Serial.println("Button Pressed!");
showButtonPressedMessage();
delayMicroseconds(1000000); //one second
attachInterrupt(digitalPinToInterrupt(13), buttonPressed, FALLING);
button_pressed=false;
}
void setupOLED(){
//Initializes the OLED display on startup
int rc=0;
rc = oledInit(&ssoled, MY_OLED, OLED_ADDR, FLIP180, INVERT, USE_HW_I2C, SDA_PIN, SCL_PIN, RESET_PIN, 400000L); // use standard I2C bus at 400Khz
if (rc != OLED_NOT_FOUND)
{
char *msgs[] = {(char *)"SSD1306 @ 0x3C", (char *)"SSD1306 @ 0x3D",(char *)"SH1106 @ 0x3C",(char *)"SH1106 @ 0x3D"};
oledFill(&ssoled, 0, 1);
oledWriteString(&ssoled, 0,0,0,msgs[rc], FONT_NORMAL, 0, 1);
oledSetBackBuffer(&ssoled, ucBackBuffer);
delay(2000);
}
showBootMessage();
delay(2000);
}
void setupPins()
{//Sets the pin modes for the LEDs and sets up the interrupt for the button.
// HSE
pinMode(9,OUTPUT);digitalWrite(9,LOW);
pinMode(10,OUTPUT);digitalWrite(10,LOW);
pinMode(11,OUTPUT);digitalWrite(11,LOW);
//PSM
pinMode(3,OUTPUT);digitalWrite(3,0);
pinMode(5,OUTPUT);digitalWrite(5,0);
pinMode(6,OUTPUT);digitalWrite(6,0);
//MI
pinMode(2,OUTPUT);digitalWrite(2,0);
pinMode(A3,OUTPUT);digitalWrite(A3,0);
pinMode(A2,OUTPUT);digitalWrite(A2,0);
//button
pinMode(13,INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(13), buttonPressed, FALLING);
}
void showButtonPressedMessage()
{
cls();
showOLEDMessage("Interrupt!",1);
showOLEDMessage(" BUTTON ",2); //line 2 is 20 wide
showOLEDMessage("PRESSED",3);
changed_display=true;
}
void showDistanceMessage()
{
String my_string="";
if (String(char_distance)=="11.7") {
my_string="Safe Distance ";
LED1="OFF";LED2="OFF";LED3="OFF";
setTheLightsPerTheVariables();
}
else if (String(char_distance)=="11.8") {
my_string="Close Object ";
LED1="RED";LED2="RED";LED3="RED";
setTheLightsPerTheVariables();
}
else
{
my_string=" " + String((char_distance)) + " ft ";
LED1="OFF";LED2="OFF";LED3="OFF";
setTheLightsPerTheVariables();
}
showOLEDMessage(&my_string[0],1);
changed_display=true;
}
void showBootMessage()
{//used in an IoT project on startup. Not used now.
cls();
showOLEDMessage(" BOOTING",1);
showOLEDMessage("",2); //line 2 is 20 wide
showOLEDMessage(" STANDBY",3);
changed_display=true;
}
void showEnterWiFiMessage()
{
cls();
showOLEDMessage("Almost There!",4);
showOLEDMessage("Fill Out Webform",6);
changed_display=true;
LED1="OFF";LED2="GREEN";LED3="GREEN";
setTheLightsPerTheVariables();
}
void showAttemptingConnection()
{
cls();
showOLEDMessage("Got WiFi!",1);
showOLEDMessage("",2); //line 2 is 20 wide
showOLEDMessage("Connecting...",4);
changed_display=true;
LED1="YELLOW";LED2="GREEN";LED3="GREEN";
setTheLightsPerTheVariables();
}
void showSetTime()
{
cls();
showOLEDMessage("Time is Set!",1);
changed_display=true;
delay(3000);
}
void showAwaitingWebForm()
{
cls();
showOLEDMessage("CONNECTED",1);
showOLEDMessage("Use Your Smart Phone",2); //line 2 is 20 wide
showOLEDMessage(" Browse: ",4);
showOLEDMessage("192.168.4.1",5);
changed_display=true;
LED1="OFF";LED2="YELLOW";LED3="GREEN";
setTheLightsPerTheVariables();
}
void showWaitingForWiFiMessage()
{//used in an IoT project on startup. Not used now.
cls();
showOLEDMessage(" No WiFi",1);
showOLEDMessage("Use Your Smart Phone",2); //line 2 is 20 wide
showOLEDMessage("Connect WiFi to" ,4);
showOLEDMessage("ARC_Ornament",5);
showOLEDMessage(" Browse: ",6);
showOLEDMessage("192.168.4.1",7);
changed_display=true;
}
void readDistanceAndAlert(){
setDistanceFloat();
if (float_distance<MIN_DISTANCE) {
alertSocialDistancing();
showPoliceLights();
LED1="OFF";LED2="OFF";LED3="OFF";
setTheLightsPerTheVariables();
cls();
}
}
void showPoliceLights()
{// A routine that simulates police flashers
for (int ii=0;ii<15;ii++) {
LED1="RED";
LED2="RED";
LED3="RED";
setTheLightsPerTheVariables();
delay(100);
if (button_pressed) break;
LED1="BLUE";
LED2="BLUE";
LED3="BLUE";
setTheLightsPerTheVariables();
delay(100);
}
}
void setTheLightsPerTheVariables()
{ //I use variables to track and set each LED's using a string. That way my code can always refer to the state of the device.
if (LED1=="RED") {
setLED(1,"RED");
}else if(LED1=="GREEN") {
setLED(1,"GREEN");
}else if (LED1=="YELLOW") {
setLED(1,"YELLOW");
}else if (LED1=="BLUE") {
setLED(1,"BLUE");
}else setLED(1,"OFF");
if (LED2=="RED") {
setLED(2,"RED");
}else if (LED2=="GREEN") {
setLED(2,"GREEN");
}else if (LED2=="YELLOW") {
setLED(2,"YELLOW");
}else if (LED2=="BLUE") {
setLED(2,"BLUE");
}else setLED(2,"OFF");
if (LED3=="RED") {
setLED(3,"RED");
}else if (LED3=="GREEN") {
setLED(3,"GREEN");
}else if (LED3=="YELLOW") {
setLED(3,"YELLOW");
}else if (LED3=="BLUE") {
setLED(3,"BLUE");
} else setLED(3,"OFF");
}
void setLED(int LED, String color)
{ //used to directly set a light color.
if (LED==1) {
if (color=="YELLOW") {
if (DEBUG) Serial.println("hit yellow");
digitalWrite(9,LOW);//
digitalWrite(10,HIGH);
digitalWrite(11,HIGH);
}
else if (color=="GREEN") {
digitalWrite(9,0);
digitalWrite(10,HIGH);//
digitalWrite(11,0);//
}
else if (color=="RED")
{
digitalWrite(9,0);
digitalWrite(10,0);//
digitalWrite(11,HIGH);//
}
else if (color=="BLUE")
{
digitalWrite(9,HIGH);//
digitalWrite(10,0);
digitalWrite(11,0);//
}
else
{
digitalWrite(9,0);//
digitalWrite(10,0);
digitalWrite(11,0);//
}
} else if (LED==2) {
//if (DEBUG) Serial.println("Current color: +" + color + " " + INTENSITY);
if (color=="YELLOW") {
digitalWrite(3,0);
digitalWrite(5,HIGH);
digitalWrite(6,HIGH);
}
else if (color=="GREEN") {
digitalWrite(3,0);
digitalWrite(5,HIGH);
digitalWrite(6,0);
}
else if (color=="RED")
{
digitalWrite(3,0);
digitalWrite(5,0);
digitalWrite(6,HIGH);
}
else if (color=="BLUE")
{
digitalWrite(3,HIGH);
digitalWrite(5,0);
digitalWrite(6,0);
}
else
{
digitalWrite(3,0);
digitalWrite(5,0);
digitalWrite(6,0);
}
} else if (LED==3) {
if (DEBUG) Serial.println("Current Color:" + color);
if (color=="YELLOW") {
digitalWrite(2,0);
digitalWrite(16,HIGH);
digitalWrite(17,HIGH);
}
else if (color=="GREEN") {
digitalWrite(2,0);
digitalWrite(16,0);
digitalWrite(17,HIGH);
}
else if (color=="BLUE")
{
digitalWrite(2,INTENSITY);
digitalWrite(16,0);
digitalWrite(17,0);
}
else if (color=="RED")
{
digitalWrite(2,0);
digitalWrite(16,HIGH);
digitalWrite(17,0);
}
else
{
digitalWrite(2,0);
digitalWrite(16,0);
digitalWrite(17,0);
}
}
}
void cls()
{ // clear the screen
oledFill(&ssoled, 0x0, 1);
}
void showOLEDMessage(char *the_message, int the_line)
{
switch (the_line)
{
case 1: oledWriteString(&ssoled, 0,16,0,the_message, FONT_NORMAL, 0, 1); break;
case 2: oledWriteString(&ssoled, 0,0,1,the_message, FONT_SMALL, 1, 1); break;
case 3: oledWriteString(&ssoled, 0,0,3,the_message, FONT_LARGE, 0, 1); break;
case 4: oledWriteString(&ssoled, 0,0,3,the_message, FONT_NORMAL, 0, 1); break;
case 5: oledWriteString(&ssoled, 0,0,4,the_message, FONT_NORMAL, 0, 1); break;
case 6: oledWriteString(&ssoled, 0,0,4,the_message, FONT_LARGE, 0, 1); break;
case 7: oledWriteString(&ssoled, 0,0,2,the_message, FONT_SMALL, 0, 1); break;//Row just under yellow above 3.
}
}
void showIdleMessage()
{ //routine to show something when not in alert mode.
if (!changed_display) return;
changed_display=false;
cls();
showOLEDMessage("YOUR STATUS",1);
showOLEDMessage("",2); //line 2 is 20 wide
showOLEDMessage("AWESOME!",3);
}
void alertSocialDistancing()
{ //Called whenever you want to go into an alert mode due to an object being too close.
cls();
String my_string=" " + String((char_distance)) + " ft ";
showOLEDMessage(&my_string[0],1);
showOLEDMessage(" !SOCIAL DISTANCE! ",2); //line 2 is 20 wide
showOLEDMessage("BACK UP!",3);
changed_display=true;
}
void parseTheResponse(String the_response)
{ // used to parse out returned webpage text for IoT projects.
// some roughhousing to split this up into our strings.
the_response=getFromHereToThere(the_response,"<span id=\"my_response\"", "</span>"); // get the lines in between the span.
Scroll=the_response.substring(0,the_response.indexOf('\r')); the_response=the_response.substring(Scroll.length()+2, the_response.length());
LED1=the_response.substring(0, the_response.indexOf('\r')); the_response=the_response.substring(LED1.length()+2, the_response.length());
LED2=the_response.substring(0, the_response.indexOf('\r')); the_response=the_response.substring(LED2.length()+2, the_response.length());
LED3=the_response.substring(0, the_response.indexOf('\r')); the_response=the_response.substring(LED3.length()+2, the_response.length());
Status=the_response.substring(0, the_response.indexOf('\r'));
if (DEBUG) Serial.println("hello"+Scroll+LED1+LED2+LED3+Status+"goodbye");
}
String getFromHereToThere(String the_str, String the_start, String the_end)
{ //This helps parse a string. It is used with IoT projects. See parseTheResponse to get a message from a web page and parse out its content
int ii=0;int start_index=-1;int end_index=-1;
for (ii=0;ii<(the_str.length()-1);ii++) {
if (the_str.substring(ii,ii+the_start.length())==the_start)
start_index=ii;
else if (the_str.substring(ii,ii+the_end.length())==the_end)
{end_index=ii; break;}
}
if (start_index==-1||end_index==-1) return the_str; else return the_str.substring(start_index+the_start.length(),end_index);
}
void setDistanceFloat()
{ //the main method to measure distance. It stores it in float_distance
float temp_distance = ((((float)ultrasonic.read())/2.54)/12); //feet
if (DEBUG) Serial.println(temp_distance);
if (temp_distance<MIN_DISTANCE)
{
//double check just in case we had a bad read.
temp_distance = ((((float)ultrasonic.read())/2.54)/12);
}
float_distance=temp_distance;
float_distance=5.5;
if (float_distance<1) float_distance=11.8;//will show "Close Object" on the display
memset(char_distance,0,7);//clear the memory of the char array named char_distance
dtoa(float_distance, char_distance, 1);//store the float as a char array named char_distance
}
char* dtoa(double dN, char *cMJA, int iP) {
//Used to convert a double to an ascii value for showing on the serial monitor.
char *ret = cMJA; long lP=1; byte bW=iP;
while (bW>0) { lP=lP*10; bW--; }
long lL = long(dN);
double dD=(dN-double(lL))* double(lP);
if (dN>=0) {
dD=(dD + 0.5);
} else
{ dD=(dD-0.5); }
long lR=abs(long(dD)); lL=abs(lL);
if (lR==lP) { lL=lL+1; lR=0; }
if ((dN<0) & ((lR+lL)>0)) { *cMJA++ = '-'; }
ltoa(lL, cMJA, 10);
if (iP>0) { while (*cMJA != '\0') { cMJA++; } *cMJA++ = '.'; lP=10;
while (iP>1) {
if (lR< lP) { *cMJA='0'; cMJA++; } lP=lP*10; iP--; }
ltoa(lR, cMJA, 10);
}
return ret;
}
void printWifiStatus() {
// Debugging method for print the SSID of the network you're attached to:
if (!DEBUG) return;
if (DEBUG) Serial.print("SSID: ");
if (DEBUG) Serial.println(WiFi.SSID());
// print your board's IP address:
IPAddress ip = WiFi.localIP();
if (DEBUG) Serial.print("IP Address: ");
if (DEBUG) Serial.println(ip);
// print the received signal strength:
long rssi = WiFi.RSSI();
if (DEBUG) Serial.print("signal strength (RSSI):");
if (DEBUG) Serial.print(rssi);
if (DEBUG) Serial.println(" dBm");
}
void TimerCallback0(void)
{ //this is a timer callback method to do whatever you like every fraction of a second. Allows a threaded approach using flag variables.
}
void TC3_Handler() {
Adafruit_ZeroTimer::timerHandler(3);
}
void timer_setup() {
//this is a timer callback method to do whatever you like every fraction of a second. Allows a threaded approach.
// Set up the flexible divider/compare
uint8_t divider = 1;
uint16_t compare = 0;
tc_clock_prescaler prescaler = TC_CLOCK_PRESCALER_DIV1;
if ((freq < 24000000) && (freq > 800)) {
divider = 1;
prescaler = TC_CLOCK_PRESCALER_DIV1;
compare = 48000000/freq;
} else if (freq > 400) {
divider = 2;
prescaler = TC_CLOCK_PRESCALER_DIV2;
compare = (48000000/2)/freq;
} else if (freq > 200) {
divider = 4;
prescaler = TC_CLOCK_PRESCALER_DIV4;
compare = (48000000/4)/freq;
} else if (freq > 100) {
divider = 8;
prescaler = TC_CLOCK_PRESCALER_DIV8;
compare = (48000000/8)/freq;
} else if (freq > 50) {
divider = 16;
prescaler = TC_CLOCK_PRESCALER_DIV16;
compare = (48000000/16)/freq;
} else if (freq > 12) {
divider = 64;
prescaler = TC_CLOCK_PRESCALER_DIV64;
compare = (48000000/64)/freq;
} else if (freq > 3) {
divider = 256;
prescaler = TC_CLOCK_PRESCALER_DIV256;
compare = (48000000/256)/freq;
} else if (freq >= 0.75) {
divider = 1024;
prescaler = TC_CLOCK_PRESCALER_DIV1024;
compare = (48000000/1024)/freq;
} else {
while (1) delay(10);
}
zerotimer.enable(false);
zerotimer.configure(prescaler, // prescaler
TC_COUNTER_SIZE_16BIT, // bit width of timer/counter
TC_WAVE_GENERATION_MATCH_PWM // frequency or PWM mode
);
zerotimer.setCompare(0, compare);
zerotimer.setCallback(true, TC_CALLBACK_CC_CHANNEL0, TimerCallback0);
zerotimer.enable(true);
}
bool GetUserWiFi(){
if (DEBUG) Serial.println("Access Point Web Server");
bool message_not_shown=true;
showWaitingForWiFiMessage();
// check for the WiFi module:
if (WiFi.status() == WL_NO_MODULE) {
if (DEBUG) Serial.println("Communication with WiFi module failed!");
// don't continue
while (true);
}
String fv = WiFi.firmwareVersion();
if (fv < WIFI_FIRMWARE_LATEST_VERSION) {
if (DEBUG) Serial.println("Please upgrade the firmware");
}
// by default the local IP address of will be 192.168.4.1
// you can override it with the following:
// print the network name (SSID);
if (DEBUG) Serial.print("Creating access point named: ");
if (DEBUG) Serial.println(ssid);
// Create open network. Change this line if you want to create an WEP network:
status = WiFi.beginAP(ssid);
while (status != WL_AP_LISTENING) {
if (DEBUG) Serial.println("Creating access point failed. Trying again.");
// don't continue
status = WiFi.beginAP(ssid);
delay(1000);
}
if (DEBUG) Serial.println("Access Point established.");
// wait 10 seconds for connection:
delay(10000);
// start the web server on port 80
server.begin();
bool done=false; bool awaiting_connection=true;
unsigned long the_start = millis();
while (!done){
// compare the previous status to the current status
if (status != WiFi.status()) {
// it has changed update the variable
status = WiFi.status();
if (status == WL_AP_CONNECTED) {
// a device has connected to the AP
if (DEBUG) Serial.println("Device connected to AP");
awaiting_connection=false;
showAwaitingWebForm();
} else {
// a device has disconnected from the AP, and we are back in listening mode
if (DEBUG) Serial.println("Device disconnected from AP");
}
}
client = server.available(); // listen for incoming clients
if (client) { // if you get a client,
bool first_hit=true; String the_form_submitted="";
if (DEBUG) Serial.println("new client"); // print a message out the serial port
String currentLine = ""; // make a String to hold incoming data from the client
while (client.connected()) { // loop while the client's connected
if (client.available()) { // if there's bytes to read from the client,
char c = client.read(); // read a byte, then
if (DEBUG) Serial.write(c); // print it out the serial monitor
if (c == '\n') { // if the byte is a newline character
// if the current line is blank, you got two newline characters in a row.
// that's the end of the client HTTP request, so send a response:
if (currentLine.length() == 0) {
awaiting_connection=false;
// HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
// and a content-type so the client knows what's coming, then a blank line:
client.println("HTTP/1.1 200 OK");
client.println("Content-type:text/html");
client.println();
// the content of the HTTP response follows the header:
client.println("<!DOCTYPE html><html lang=\"en\"><head><title>ARC Ornament Setup</title>");
client.println("<meta charset=\"utf-8\">");
client.println("<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
client.println("<link rel=\"stylesheet\" href=\"https://maxcdn.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css\">");
client.println("<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js\"></script>");
client.println("<script src=\"https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js\"></script>");
client.println("<script src=\"https://maxcdn.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js\"></script>");
client.println("</head>");
client.println("<body>");
if (first_hit) {
client.println("<div class=\"jumbotron text-left\">");
client.println("<h3 class='alert alert-info'>ARC Ornament Setup:</h3>");
client.println("<p class='lead'>Here you will specify your local WiFi settings that your ARC Desk Ornament will use to connect to the internet.</br></br></p>");
client.println("</div>");
client.println("<form method=Get class='pl-2 pr-2' action='/'>");
client.println("<div class='container'>");
client.println("<div class='row'><div class='col-sm-2'><b>Enter your WiFi SSID:</b><input class='form-control' name=ssid type=text/></div></div>");
client.println("<div class='row'><div class='col-sm-2'><br/><b>Enter your WiFi Password:</b><input class='form-control' name=pwd></div></div>");
client.println("<input type=hidden name=ender value=ender/>");
client.println("<div class='row'><div class='col-sm-12'><br/><button class='btn btn-primary btn-block' type=submit>Submit</button></div></div>");
client.println("</div>");
client.println("</form></br></br></br></br></br>");
if (message_not_shown) {
message_not_shown=false;
showEnterWiFiMessage();
}
}
else {
client.println("<div class=\"jumbotron text-left\">");
client.println("<div class='display-4 alert alert-success text-center'><b>GREAT JOB!</b></div>");
client.println("<p class='lead'>Now look at your ARC Desk Ornament display for further instructions.<br/><br/>If it shows an error, you may have a typo or unexpected special characters causing a conflict. Unplug or press the reset button on top of the ARC Desk Ornament and try the process again.</p>");
client.println("</div>");
previous_millis=millis();
my_ssid=getSSID(the_form_submitted);
my_pass=getPassword(the_form_submitted);
client.println("<ul><li>SSID: " + my_ssid + "</li><li>Password: " + my_pass + "</li></ul>");
done=true;
if (my_ssid=="") showSetTime(); else showAttemptingConnection();
}
client.println("</body></html>");
// The HTTP response ends with another blank line:
client.println();
// break out of the while loop:
break;
}
else { // if you got a newline, then clear currentLine:
currentLine = "";
}
}
else if (c != '\r') { // if you got anything else but a carriage return character,
currentLine += c; // add it to the end of the currentLine
}
// Handle the form posted back to the page:
if (currentLine.endsWith("ender=")) {
first_hit=false;
the_form_submitted=currentLine;
}
}
}
// close the connection:
client.stop();
if (DEBUG) Serial.println("client disconnected");
}
if ((millis()-the_start)>10000&&awaiting_connection) return(false);
}
if (my_ssid=="") return(false); else return(true);
}
String getPassword(String the_line){
int start=0; int end=0;
start=the_line.indexOf("pwd=")+4;
end=the_line.indexOf("ender=")-1;
String the_ans=the_line.substring(start,end);
the_ans.replace("+","");
the_ans.replace("%23","#");
the_ans.replace("%24","$");
the_ans.replace("%21","!");
the_ans.replace("%40","@");
the_ans.trim();
return(the_ans);
}
String getSSID(String the_line){
int start=0; int end=0;
start=the_line.indexOf("ssid=")+5;
end=the_line.indexOf("pwd=")-1;
String the_ans=the_line.substring(start,end);
the_ans.replace("+"," ");
the_ans.replace("%23","#");
the_ans.replace("%24","$");
the_ans.replace("%21","!");
the_ans.replace("%40","@");
the_ans.replace("%E2%80%99","'");
the_ans.trim();
return(the_ans);
}
String getHour(String the_line){
int start=0; int end=0;
start=the_line.indexOf("hour=")+5;
end=the_line.indexOf("min=")-1;
String the_ans=the_line.substring(start,end);
the_ans.trim();
return(the_ans);
}
String getMinute(String the_line){
int start=0; int end=0;
start=the_line.indexOf("minute=")+7;
end=the_line.indexOf("ender=")-1;
String the_ans=the_line.substring(start,end);
the_ans.trim();
return(the_ans);
}
//https://query1.finance.yahoo.com/v8/finance/chart/psx
bool GetStockPriceFromYahoo() {
//Initialize serial and wait for port to open:
// attempt to connect to Wifi network:
connectToWiFi();
WiFiSSLClient clientSSL;
if (DEBUG) Serial.println("Starting connection to Yahoo...");
// if you get a connection, report back via serial:
if (clientSSL.connect("query1.finance.yahoo.com", 443)) {
if (DEBUG) Serial.println("connected to time server");
// Make a HTTP request:
clientSSL.println("GET /v8/finance/chart/psx HTTP/1.1");
clientSSL.println("Host: query1.finance.yahoo.com");
clientSSL.println("User-Agent: ARC/1.0");
clientSSL.println("Connection: close");
clientSSL.println();
}
else
{
if (DEBUG) Serial.println("Failed to get anything");
return (false);
}
String the_response="";
while (!clientSSL.available()) delay(1000); //pause until data is sent from the server.
int bailer=0;
while (clientSSL.available()) {
char c = clientSSL.read();
the_response+=c;
if (DEBUG) Serial.write(c);
if (bailer++>1500) break;
}
if (DEBUG) Serial.println("\n\nFinished Receiving from Yahoo");
if (!clientSSL.connected()) {
if (DEBUG) Serial.println();
if (DEBUG) Serial.println("Disconnecting from yahoo.");
clientSSL.stop();
}
//"regularMarketPrice":69.28,"chartPreviousClose"
int the_start=the_response.indexOf("\"regularMarketPrice\"");
if (the_start>-1)
{
the_response=the_response.substring(the_start);
if (DEBUG) Serial.println("Stock Step1:" + the_response);
the_response=getFromHereToThere(the_response, "regularMarketPrice\":", ",");
if (DEBUG) Serial.println("Stock:" + the_response);
the_stock=the_response;
millis_when_stock_set=millis();
return (true);
}
else
{
if (DEBUG) Serial.println("Did not get valid response from Yahoo server.");
return (false);
}
}
bool GetTimeFromWorldTimeAPI() {
//Initialize serial and wait for port to open:
millis_when_time_set=millis()+(unsigned long)4000;//putting it here so that it won't over hit the time server if its not connecting.
// attempt to connect to Wifi network:
connectToWiFi();
if (DEBUG) Serial.println("Starting connection to server...");
// if you get a connection, report back via serial:
if (client.connect("worldclockapi.com", 80)) {
if (DEBUG) Serial.println("connected to time server");
// Make a HTTP request:
client.println("GET /api/json/cst/now HTTP/1.1");
client.println("Host: worldclockapi.com");
client.println("User-Agent: ARC/1.0");
client.println("Accept: text/html");
client.println("Connection: close");
client.println();
}
else
{
if (DEBUG) Serial.println("Failed to get anything");
return (false);
}
// if there are incoming bytes available
// from the server, read them and print them:
String the_response="";
while (!client.available()) delay(1000); //pause until data is sent from the server.
while (client.available()) {
char c = client.read();
the_response+=c;
if (DEBUG) Serial.write(c);
}
if (DEBUG) Serial.println("\n\nFinished Receiving from Time Website");
// if the server's disconnected, stop the client:
if (!client.connected()) {
if (DEBUG) Serial.println();
if (DEBUG) Serial.println("disconnecting from server.");
client.stop();
}
int the_start=the_response.indexOf("\"currentDateTime\"");//example "currentDateTime":"2020-12-23T07:28-06:00",
if (the_start>-1)
{
the_time=the_response.substring(the_start+19,the_start+35);
if (DEBUG) Serial.println("The time: " + the_time);
if (DEBUG) Serial.println("Checker:" + the_time.substring(11,13));
int the_hour=(the_time.substring(11,13)).toInt();
int the_minute=(the_time.substring(14,16)).toInt();
int the_second=0;
int the_year=(the_time.substring(2,4)).toInt();
int the_month=(the_time.substring(5,7)).toInt();
int the_day=(the_time.substring(8,10)).toInt();
if (DEBUG) Serial.println("Hour:" + String(the_hour));
if (DEBUG) Serial.println("Minute:" + String(the_minute));
rtc.setHours(the_hour);
rtc.setMinutes(the_minute);
rtc.setSeconds(the_second);
rtc.setMonth(the_month);
rtc.setYear(the_year);
rtc.setDay(the_day);
return (true);
}
else
{
if (DEBUG) Serial.println("Did not get valid response from time server.");
return (false);
}
}
void GetJokeOfTheDayAPI() {
//Initialize serial and wait for port to open:
if (DEBUG) Serial.println("About to do it!");
// attempt to connect to Wifi network:
connectToWiFi();
if (DEBUG) Serial.println("Starting connection to server...");
// if you get a connection, report back via serial:
if (client.connect("api.jokes.one", 80)) {
if (DEBUG) Serial.println("connected to server");
// Make a HTTP request:
client.println("GET /jod HTTP/1.1");
client.println("Host: api.jokes.one");
client.println("Connection: close");
client.println();
delay(2000);
} else {if (DEBUG) Serial.println("Failed to get anything");}
// if there are incoming bytes available
// from the server, read them and print them:
String the_response="";
while (client.available()) {
char c = client.read();
the_response+=c;
if (DEBUG) Serial.write(c);
}
if (DEBUG) Serial.println("Finished Receiving from Joke Site");
// if the server's disconnected, stop the client:
if (!client.connected()) {
if (DEBUG) Serial.println();
if (DEBUG) Serial.println("disconnecting from server.");
client.stop();
}
if (DEBUG) Serial.println("Made it this far." );
if (DEBUG) Serial.println(the_response);
if (the_response.indexOf("\"text\"")>0) {
the_joke=getFromHereToThere(the_response, "\"text\":\"", "\"}}],\"copyright");
} else {
the_joke="Why did the chicken cross the road? To get to the other side.";
}
if (DEBUG) Serial.println("Checker:" + the_joke);
joke_time_retrieved=millis();
}
//https://api.darksky.net/forecast/4ba6263cd7a889f74f11d981612dcb22/38.7654216,-89.9731608
void connectToWiFi(){
WiFi.end();//just in case it was already on.
delay(500);
char the_ssid[my_ssid.length()+1];
char the_pass[my_pass.length()+1];
my_ssid.toCharArray(the_ssid,my_ssid.length()+1);
my_pass.toCharArray(the_pass,my_pass.length()+1);
status = WiFi.begin(the_ssid, the_pass);
delay(3000);
while (status != WL_CONNECTED) {
if (DEBUG) Serial.print("Attempting to connect to SSID: ");
if (DEBUG) Serial.println(my_ssid);
// Connect to WPA/WPA2 network. Change this line if using open or WEP network:
status = WiFi.begin(the_ssid, the_pass);
// wait 10 seconds for connection:
delay(10000);
}
if (DEBUG) Serial.println("Connected to wifi");
}
<%@ Page Title="ARC" Language="VB"%>
<script runat="server">
Sub Page_Load(Sender As Object, e As EventArgs)
If Request.QueryString("User") = "sean" Then
my_response.Text = "Hello, World!" & vbCrLf & "GREEN" & vbCrLf & "YELLOW" & vbCrLf & "BLACK" & vbCrLf
if Request.Querystring("Action")="Alert" then my_response.text+="ALERT" else my_response.text+="IDLE"
Else
my_response.Text = "I don't know you!" & vbCrLf
End If
End Sub
</script>
<asp:label runat="server" ID="my_response" />
#include <RTCZero.h>
#include <Ultrasonic.h>
#include <ss_oled.h>
#include <SPI.h>
#include <WiFiNINA.h>
#include "Adafruit_ZeroTimer.h"
#define USE_BACKBUFFER
#ifdef USE_BACKBUFFER
static uint8_t ucBackBuffer[1024];
#else
static uint8_t *ucBackBuffer = NULL;
#endif
#define SDA_PIN 18
#define SCL_PIN 19
#define RESET_PIN -1
#define OLED_ADDR 0x3c
#define FLIP180 0
#define INVERT 0
#define USE_HW_I2C 0
#define MY_OLED OLED_128x64
#define OLED_WIDTH 128
#define OLED_HEIGHT 64
#define MIN_DISTANCE 5
Ultrasonic ultrasonic(20,21); //there was an error on the original board (7/2020). I landed what should have been 21 on the 5V and 20 on 21.
unsigned long joke_delay=(unsigned long)0;
SSOLED ssoled;
String line_one="";
String line_two="";
String line_three="";
String Scroll="";
String LED1="GREEN";
String LED2="GREEN";
String LED3="GREEN";
String Status="";
boolean changed_display=false; boolean button_pressed=false;
int INTENSITY=255;
//Ultrasonic global variables
double float_distance;
char char_distance[7];
//Real Time Clock
RTCZero rtc;
//wifi global variables
char ssid[] = "ARC_ORNAMENT"; // your network SSID (name)
String my_ssid="YOURSSID";
String my_pass="YOURPASS";
int status = WL_IDLE_STATUS;
WiFiServer server(80);
WiFiClient client;
unsigned long millis_when_time_set=millis(); unsigned long joke_time_retrieved=0;
unsigned long millis_when_stock_set=millis();
int current_seconds=0;
String the_time="9:23";
String the_date="01/01";
String the_stock="0";
//timer globabl variables
float freq = 12.0; // 1 KHz
Adafruit_ZeroTimer zerotimer = Adafruit_ZeroTimer(3);
boolean heading_up=true;
unsigned long previous_millis=millis();
bool online=false;
String the_joke;
IoT Smart Desk Distance and KPI Meter
*PCBWay community is a sharing platform. We are not responsible for any design issues and parameter issues (board thickness, surface finish, etc.) you choose.
- Comments(0)
- Likes(4)
- PCBWay happy Aug 25,2021
- PCBWay Support Team Aug 25,2021
- Engineer Aug 23,2021
- Sean Miller Aug 22,2021
- 1 USER VOTES
- YOUR VOTE 0.00 0.00
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
-
9design
-
6usability
-
8creativity
-
7content
More by Sean Miller
- World's First Published Open-source Arduino Nano RP2040 Connect Drone Flight Controller INTRODUCTIONThe Arduino Nano RP2040 Connect comes with a built in IMU. It's fast speed and ability t...
- Spiderman Spidey Sense and Webslinger INTRODUCTIONSince I was six, I thought it would be cool to make my own web caster. Not knowing much ...
- LaserCutter_PCB_2022-02-27 GRBL shield for the Arduino Uno.Designed to allow wiring up your motors and separate motor drivers. ...
- IoT Smart Desk Distance and KPI Meter HTTPS://WWW.RAISINGAWESOME.SITEINTRODUCTIONThis IoT project was born out of the uncertain times pres...
-
-
-
kmMiniSchield MIDI I/O - IN/OUT/THROUGH MIDI extension for kmMidiMini
109 0 0 -
DIY Laser Power Meter with Arduino
157 0 2 -
-
-
Box & Bolt, 3D Printed Cardboard Crafting Tools
152 0 2 -
-
A DIY Soldering Station Perfect for Learning (Floppy Soldering Station 3.0)
544 0 2