亲,“电路城”已合并升级到更全、更大、更强的「新与非网」。点击查看「新与非网」

本网页已闲置超过3分钟,按键盘任意键或点击空白处,即可回到网页

基于物联网云的闹钟

发布时间:2022-10-20
分享到:

基于物联网云的闹钟

发布时间:2022-10-20
分享到:

通过物联网云设置你的闹钟,准时开始你的一天。

这款收音机闹钟的音量非常大,还可以作为蓝牙扬声器使用。但是,也有一些问题随之而来:

  • 闹钟的照明太亮了,不能把它放在你睡觉的地方(记住,这是一个闹钟),没有开关,也不可能调暗它。
  • 这个钟不是无线电控制的,你得手动设定。
  • 闹钟时间设置为12小时的时钟周期。如果你把闹钟调到早上6点,它也会在下午6点响。鉴于手动时钟的设置,我也无法非常准确地设置它。
  • 时钟(和闹钟)由电池供电,而收音机的其他部分则由墙上的适配器供电。

就我而言,我还遇到过闹钟在半夜响起的问题。

我的概念包括三个基本改进:

  • 得到准确的时间
  • 相应地设置时钟
  • 设定一个准确的闹钟时间,让闹钟(在这种情况下是收音机)在那个时间响起

对于设置一个模拟时钟,我了解到难点在于知道时钟指针显示的时间。在重新启动后设置时钟时,解决这个问题的一个方法是找到一个参考位置,比如12点,然后开始数步数。

为了设置一个准确的闹钟时间,我决定使用收音机的止闹按钮和隐藏在闹钟表面后面的显示器。

我的第一个尝试是使用原来的时钟机芯,并添加一个带有Arduino Nano、Micro OLED和DCF 77接收器的“背包”,以获得准确的时间。不过不巧的是,DCF信号的接收是不够的,一旦组件被放入外壳与所有的电线从收音机周围。此外,我无法通过使用原始机芯的闹钟检测到时钟指针的参考位置。正如上面提到的,闹钟在不同时间响起,首先就是问题的一部分。

对于一个总是在室内的项目,特别是在Arduino IoT云公共测试版宣布后,我决定包括WiFi连接,以获得准确的时间。

接下来要解决的问题是找到时钟指针的参考位置。几次使用霍尔传感器/磁力计和手上的磁铁的尝试都是可以的,但在准确性和可重复性方面没有达到我的预期。幸运的是,在直接联系制造商后,我收到了一个集成光电耦合器的时钟步进电机样品,用于参考检测。

最后,在Arduino Nano 33物联网发布后,我把原型机中的MKR WiFi 1010换成了Nano,它的外形更小。

闹钟时间(小时和分钟)可以通过贪睡按钮设置,也可以通过仪表盘设置,就像宣传中所说的那样。

这是我的第一个爱好项目与3d打印零件和我的第一个定制PCB。作为PCB设计的初学者,Fritzing易于使用的PCB特性非常有帮助。

时钟单元比较

硬件连接

印刷电路板

机械设计

Code for IoT Alarm Clock:

/*
  Sketch generated by the Arduino IoT Cloud Thing "Alarm_Clock"
  https://create.arduino.cc/cloud/things/...

  Arduino IoT Cloud Properties description

  The following variables are automatically generated and updated when changes are made to the Thing properties

  int alarmHour;
  int gmt;
  String wifi_time;
  int alarmMinute;
  int readClockMaxValue;

  Properties which are marked as READ/WRITE in the Cloud Thing will also have functions
  which are called when their values are changed from the Dashboard.
  These functions are generated with the Thing and added at the end of this sketch.
*/

#include "thingProperties.h"

//OLED example
/******************************************************************************
   MicroOLED_Demo.ino
   SFE_MicroOLED Library Demo
   Jim Lindblom @ SparkFun Electronics
   Original Creation Date: October 27, 2014

   This sketch uses the MicroOLED library to draw a 3-D projected
   cube, and rotate it along all three axes.

   Development environment specifics:
    Arduino 1.0.5
    Arduino Pro 3.3V
    Micro OLED Breakout v1.0

   This code is beerware; if you see me (or any other SparkFun employee) at the
   local, and you've found our code helpful, please buy us a round!

   Distributed as-is; no warranty is given.
 ******************************************************************************/

// wifi rtc
/**********************************************************
  MKR1000 - MKR WiFi 1010 - MKR VIDOR 4000 WiFi RTC

  This sketch asks NTP for the Linux epoch and sets the internal Arduino MKR1000's RTC accordingly.

  created 08 Jan 2016
  by Arturo Guadalupi <a.guadalupi@arduino.cc>

  modified 26 Sept 2018

  http://arduino.cc/en/Tutorial/WiFiRTC
  This code is in the public domain.
*/

//pins

/*5-pin jst
  black "RET" -> HV side (Logic Level Converter) -> D10 OUTPUT to arm alarm PIN RET_SET
  brown "SET" -> HV side (Logic Level Converter) -> D10 OUTPUT to arm alarm PIN RET_SET
  red -> "GND" -> (source)
  orange -> "6V" -> (source)
  yellow -> "ALARM" -> x
*/
#define RET_SET 10 //black and brown wire on jst 5 connector

/* 7-pin-jst
  black -> x
  brown -> x
  red -> GND
  orange -> x
  yellow -> D9 -> INPUT from button on backside PIN jst_7_yellow
  green -> D8 -> OUTPUT to arm and engage alarm PIN jst_7_green
  blue -> x - used to be battery voltage. if provided 3.3v terrible things happen
*/

#define jst_7_green 8 // green OUTPUT to arm and engage alarm
#define jst_7_yellow 9 //yellow INPUT from button on backside

/* clock / motor driver
  BI1 -> D11
  BI2 -> D12
  Em (Emitter) on clock for predefined position -> A0 PIN EM
*/
#define BI1 11
#define BI2 12
#define read_clock A0

/* 2-pin-jst
  blue -> 3.3V Input
  red -> D7 INPUT reads snooze button PIN snooze
*/
#define snooze 7 //snooze button on top

/* o-led
  GND
  3.3V
  SDI -> SDA i2c  A4
  SCK -> SCL i2c  A5
  RST -> D6 OLED reset PIN PIN_RESET
*/
#define PIN_RESET 6 //OLED reset

/* front panel
  red -> 3.3V Input
  black -> GND Input
  yellow -> D5 INPUT Button sleep PIN sleep | PLUG IN ORANGE CABLE FROM FRONT PANEL
  orange -> D4 INPUT Button Alarm armed pressed PIN arm_alarm | PLUG IN YELLOW CABLE FROM BUTTON BOARD
*/
#define arm_alarm 4 //button from small front panel
#define sleep 5 //button sleep on front panel


//alarm in tivoli
boolean alarm_armed = false;
boolean alarm_engaged = false;

boolean last_button_state;
unsigned long millis_when_button_was_not_pressed;
unsigned long millis_when_display_was_lit = 0;
boolean display_on = false;

unsigned long button_press_duration = 800; // milliseconds button is held down when pressing it


//clock

unsigned long epoch;
unsigned long millis_of_last_epoch;
int millis_until_next_epoch = 300000; //five minutes

int displayedMinute = 0;
int displayedHour = 0;


unsigned long millis_pin_status_clocks_hands = millis();

int pin_status_clocks_hands = 1;
bool clock_in_predefined_position = false; //true if clock is displaying 12:00 at 12:00

int read_clock_value_for_predefined_position = 370; //test with ac/dc power supply was 312, 302, 305, 270, 387, 400 ..

bool clock_looking_for_reference_position = true;

bool stay_in_reference_position = true;
unsigned long millis_staying_in_reference_position = millis();

bool set_time_button_was_pressed = false;

//int times to do the math
int minuteNow = 0;
int hourNow = 0;

//int alarmMinute = 0; now as cloud property
//int alarmHour = 6; now as cloud property


//OLED

#include <Wire.h>  // Include Wire if you're using I2C
#include <SFE_MicroOLED.h>  // Include the SFE_MicroOLED library

#define DC_JUMPER 1 //like in hardware setup on board, position of solder jumper
//reset defined above
/*
   A5 - SCL
   A4 - SDA
*/
MicroOLED oled(PIN_RESET, DC_JUMPER);    // I2C declaration


//wifi real time clock

#include <RTCZero.h>

RTCZero real_time_clock;


void setup()
{
  // Initialize serial and wait for port to open:
  Serial.begin(9600);
  // This delay gives the chance to wait for a Serial Monitor without blocking if none is found
  delay(1500);

  // Defined in thingProperties.h
  initProperties();

  // Connect to Arduino IoT Cloud
  ArduinoCloud.begin(ArduinoIoTPreferredConnection);

  /*
     The following function allows you to obtain more information
     related to the state of network and IoT Cloud connection and errors
     the higher number the more granular information you’ll get.
     The default is 0 (only errors).
     Maximum is 4
  */
  setDebugMessageLevel(2);
  ArduinoCloud.printDebugInfo();

  delay(10000); //necessary for serial routine to run see https://forum.arduino.cc/index.php?topic=605436.0

  pinMode(LED_BUILTIN, OUTPUT);

  //tivoli and arduino pins

  /*5-pin jst
    black "RET" -> HV side (Logic Level Converter) -> D10 OUTPUT to arm alarm PIN RET_SET
    brown "SET" -> HV side (Logic Level Converter) -> D10 OUTPUT to arm alarm PIN RET_SET
    red -> "GND" -> (source)
    orange -> "6V" -> (source)
    yellow -> "ALARM" -> x
  */
  pinMode(RET_SET, OUTPUT);

  /* 7-pin-jst
    black -> x
    brown -> x
    red -> GND
    orange -> x
    yellow -> D9 -> INPUT from button on backside PIN jst_7_yellow
    green -> D8 -> OUTPUT to arm and engage alarm PIN jst_7_green
    blue -> x - used to be battery voltage. if provided 3.3v terrible things happen
  */
  pinMode(jst_7_yellow, INPUT); //INPUT Set Alarm from Button on backside, analog 0 if button is pressed
  pinMode(jst_7_green, OUTPUT);

  /* 2-pin-jst
    blue -> 3.3V Input
    red -> D7 INPUT reads snooze button PIN snooze
  */
  pinMode(snooze, INPUT); //reads snooze button

  /* clock / motor driver
    BI1 -> D11
    BI2 -> D12
    Em (Emitter) on clock for predefined position -> A0 PIN read_clock
  */
  pinMode(BI1, OUTPUT);
  pinMode(BI2, OUTPUT);
  pinMode(read_clock, INPUT);

  digitalWrite(BI1, LOW);
  digitalWrite(BI2, LOW);

  /* o-led
    GND
    3.3V
    SDI -> SDA i2c  A4
    SCK -> SCL i2c  A5
    RST -> D6 OLED reset PIN PIN_RESET
  */
  //PIN_RESET used in oled library

  /* front panel
    red -> 3.3V Input
    black -> GND Input
    yellow -> D3 INPUT Button Alarm armed pressed PIN arm_alarm | PLUG IN YELLOW CABLE FROM BUTTON BOARD
    orange -> D2 INPUT Button sleep PIN sleep | PLUG IN ORANGE CABLE FROM FRONT PANEL
  */

  pinMode(arm_alarm, INPUT); //INPUT Button Alarm armed pressed | PLUG IN GREAY CABLE FROM BUTTON BOARD
  pinMode(sleep, INPUT); //reads sleep button


  //oled

  oled.begin();    // Initialize the OLED
  oled.clear(ALL); // Clear the display's internal memory
  oled.clear(PAGE); // Clear the buffer.

  //oled.setFontType(1);
  //oled.flipHorizontal(1);
  //oled.flipVertical(1);
  

  //wifi rtc

  real_time_clock.begin();

  readClockMaxValue=0;

  setRTC(); // get time/epoch from ntp server


  //setup complete
  print_text("Hallo");


}

void loop()
{
  ArduinoCloud.update();
  // Your code here


  //get time

  minuteNow = real_time_clock.getMinutes();
  hourNow = real_time_clock.getHours() + gmt;
  
  if (hourNow > 23)
  {
    hourNow = hourNow - 24;
  }

  wifi_time = String(hourNow) + ":" + String(minuteNow);
  
  if ((millis_of_last_epoch + millis_until_next_epoch) <= millis()) //get new time/epoch
  {
    setRTC();
  }
  

  //for debugging
  //Serial.println(wifi_time);

  if(analogRead(read_clock) > readClockMaxValue)
  {
    readClockMaxValue = analogRead(read_clock);
  }


  //set off alarm
  //if alarm time == time now AND alarm is armed
  if (((minuteNow >= alarmMinute) && ((minuteNow <= alarmMinute + 4 )) && (alarmHour == hourNow)) && (alarm_armed == true))
  {

    //show alarm time
    if (alarm_engaged == false && display_on == false)
    {
      millis_when_display_was_lit = millis(); //display will light up
    }

    alarm_engaged = true;
  }
  else
  {
    alarm_engaged = false;
  }


  //check if displayed time in clock is equal to time
  if (analogRead(read_clock) >= read_clock_value_for_predefined_position)
  {
    clock_in_predefined_position = true;
    digitalWrite(LED_BUILTIN, HIGH);

    if (clock_looking_for_reference_position == true)
    {
      clock_looking_for_reference_position = false;
      displayedMinute = 0;

      if (hourNow < 12)
      {
        displayedHour = 0;
      }
      else
      {
        displayedHour = 12;
      }
      
      //stay in reference position for two seconds
      stay_in_reference_position = true;
      millis_staying_in_reference_position = millis();

    }

  }
  else
  {
    clock_in_predefined_position = false;
    digitalWrite(LED_BUILTIN, LOW);
  }


  //check if clock is on time
  if (hourNow == 12 && minuteNow == 0)
  {
    if (clock_in_predefined_position == false)
    {
      readClockMaxValue = 0;
      clock_looking_for_reference_position = true;
    }
  }

  //stay in reference position for two seconds
  if(stay_in_reference_position == true and millis() - millis_staying_in_reference_position >= 2000)
  {
    stay_in_reference_position = false;
  }

  //move clock's hands if necessary
  if (clock_looking_for_reference_position == true)
  {
    move_clocks_hand();
  }
  else
  {
    if (stay_in_reference_position == false)
    {
      //if displayed time is five minutes (or less) early, wait for it, else, move hands
      if(((displayedHour*60+displayedMinute) < (hourNow*60+minuteNow)) || ((displayedHour*60+displayedMinute) > (hourNow*60+minuteNow+5)))
      {
        move_clocks_hand();
      }
    }
  }

  /*
    //set clock manually
    if (digitalRead(jst_7_yellow) == HIGH)
    {

    }
  */


  //set alarm time
  //if button is pressed to change wake up time at least for press duration
  if (digitalRead(snooze) == HIGH)
  {
    //turn on display or keep it on respectively (display is already on when alarm is engaged)
    millis_when_display_was_lit = millis(); //turns on display

    //if wake up time is displayed and button is pressed again
    if ((display_on == true) && (last_button_state == 0))
    {
      //change wake up time
      alarmMinute = alarmMinute + 5;
    }

    //if wake up time is displayed and button still pressed
    else if ((display_on == true) && (millis() >= millis_when_button_was_not_pressed + button_press_duration) && (millis() < millis_when_button_was_not_pressed + 3 * button_press_duration))
    {
      //change wake up time
      alarmMinute = alarmMinute + 5;
    }

    //if wake up time is displayed and button is pressed for over three turns, speed up wake up time count
    else if ((display_on == true) && (millis() >= millis_when_button_was_not_pressed + 3 * button_press_duration) && (millis() < millis_when_button_was_not_pressed + 6 * button_press_duration))
    {
      //change wake up time
      alarmMinute = alarmMinute + 15;
    }

    //if wake up time is displayed and button is pressed for over six turns, speed up wake up time count even more
    else if ((display_on == true) && (millis() >= millis_when_button_was_not_pressed + 6 * button_press_duration))
    {
      //change wake up time
      alarmMinute = alarmMinute + 30;
    }

    //weck_Minute = alarmMinute; weck_minute renamed to alarmMinute


  } // if button is pressed (sleep button also changes wake up time)
  else
  {
    millis_when_button_was_not_pressed = millis(); //to determine how long button is held down from when it wasn't pressed yet
  }

  //track last button state (snooze)
  last_button_state = digitalRead(snooze);

  if (alarmMinute >= 60)
  {
    alarmMinute = 0;
    alarmHour = alarmHour + 1;

    if (alarmHour == 24)
    {
      alarmHour = 0;
    }

    //weck_Stunde = alarmHour; weck_Stunde renamed to alarmHour

  }


  //display alarm time
  if ((millis() - millis_when_display_was_lit) <= 4000)
  {
    display_alarm_time();
  }
  else
  {
    stop_displaying_alarm_time();
  }


  //arm alarm
  //if alarm is armed (button is pressed)
  if (digitalRead(arm_alarm) == HIGH) //button pressed to arm alarm
  {

    //show alarm time
    if (alarm_armed == false && display_on == false)
    {
      millis_when_display_was_lit = millis(); //display will light up
    }

    alarm_armed = true;
  }
  else if (digitalRead(arm_alarm) == LOW)
  {
    alarm_armed = false;
  }


  //play radio or not according to alarm settings

  digitalWrite(jst_7_green, HIGH);
  digitalWrite(RET_SET, HIGH);

  if (alarm_armed == true && alarm_engaged == false) // don't play radio, waiting for alarm to engage
  {
    digitalWrite(jst_7_green, LOW);
    digitalWrite(RET_SET, LOW);
  }

}


void display_alarm_time()
{
  //display wake up time

  oled.setFontType(3); // max font 5 columns 1 row
  oled.flipHorizontal(1);
  oled.flipVertical(1);

  oled.setCursor(0, 0);  // Set the text cursor to the upper-left of the screen.
  if (alarmHour < 10)
  {
    oled.print("0");
    oled.print(alarmHour);
  }
  else
  {
    oled.print(alarmHour);
  }

  oled.print(":");      // Print ":"

  if (alarmMinute < 10)
  {
    oled.print("0");
    oled.print(alarmMinute);
  }
  else
  {
    oled.print(alarmMinute);
  }

  oled.display();

  display_on = true;

}

void stop_displaying_alarm_time()
{
  oled.clear(ALL); // Clear the display's internal memory
  oled.clear(PAGE); // Clear the buffer.
  display_on = false;
}

void print_text(String text)
{

  oled.setFontType(1); // max font 5 columns 1 row
  oled.flipHorizontal(1);
  oled.flipVertical(1);

  oled.setCursor(0, 0);  // Set the text cursor to the upper-left of the screen.

  oled.print(text);

  oled.display();

  delay(5000); //display for 5 seconds

  oled.clear(ALL); // Clear the display's internal memory
  oled.clear(PAGE); // Clear the buffer.

}

void move_clocks_hand()
{
  //millis_pin_status_clocks_hands defaults millis in setup
  //BI1, BI2 default LOW in setup

  if (millis() >= millis_pin_status_clocks_hands + 100)
  {
    //pin_status_clocks_hands defaults to 1
    switch (pin_status_clocks_hands)
    {
      case 1:
        {
          pin_status_clocks_hands = 2;
          digitalWrite(BI1, HIGH);
          digitalWrite(BI2, LOW);
        }
        break;
      case 2:
        {
          pin_status_clocks_hands = 3;
          digitalWrite(BI1, LOW);
          digitalWrite(BI2, LOW);
        }
        break;
      case 3:
        {
          pin_status_clocks_hands = 4;
          digitalWrite(BI1, LOW);
          digitalWrite(BI2, HIGH);
        }
        break;
      case 4:
        {
          pin_status_clocks_hands = 1;
          digitalWrite(BI1, LOW);
          digitalWrite(BI2, LOW);

          displayedMinute++;

          if (displayedMinute == 60)
          {
            displayedHour = displayedHour + 1;
            displayedMinute = 0;
          }
          if (displayedHour == 24)
          {
            displayedHour = 0;
          }
        }
        break;
    }
    millis_pin_status_clocks_hands = millis();
  }
}


void onGmtChange() {
  // Do something
}


void onAlarmMinuteChange() {
  // Do something
}


void onAlarmHourChange() {
  // Do something
}

void setRTC() {
  
  epoch = WiFi.getTime();
  
  real_time_clock.setEpoch(epoch);
  
  millis_of_last_epoch = millis();

  
}

如果您对此项目有任何想法、意见或问题,请在下方留言。

以上内容翻译自网络,原作者:MoritzDornseifer,如涉及侵权,可联系删除。

加入微信技术交流群

技术交流,职业进阶

关注与非网服务号

获取电子工程师福利

加入电路城 QQ 交流群

与技术大牛交朋友

讨论