Initial commit.

This commit is contained in:
Dirk Jahnke 2018-10-18 21:05:40 +02:00
commit 3b107cd653
8 changed files with 436 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
.pioenvs
.piolibdeps
.clang_complete
.gcc-flags.json

67
.travis.yml Normal file
View File

@ -0,0 +1,67 @@
# Continuous Integration (CI) is the practice, in software
# engineering, of merging all developer working copies with a shared mainline
# several times a day < https://docs.platformio.org/page/ci/index.html >
#
# Documentation:
#
# * Travis CI Embedded Builds with PlatformIO
# < https://docs.travis-ci.com/user/integration/platformio/ >
#
# * PlatformIO integration with Travis CI
# < https://docs.platformio.org/page/ci/travis.html >
#
# * User Guide for `platformio ci` command
# < https://docs.platformio.org/page/userguide/cmd_ci.html >
#
#
# Please choose one of the following templates (proposed below) and uncomment
# it (remove "# " before each line) or use own configuration according to the
# Travis CI documentation (see above).
#
#
# Template #1: General project. Test it using existing `platformio.ini`.
#
# language: python
# python:
# - "2.7"
#
# sudo: false
# cache:
# directories:
# - "~/.platformio"
#
# install:
# - pip install -U platformio
# - platformio update
#
# script:
# - platformio run
#
# Template #2: The project is intended to be used as a library with examples.
#
# language: python
# python:
# - "2.7"
#
# sudo: false
# cache:
# directories:
# - "~/.platformio"
#
# env:
# - PLATFORMIO_CI_SRC=path/to/test/file.c
# - PLATFORMIO_CI_SRC=examples/file.ino
# - PLATFORMIO_CI_SRC=path/to/test/directory
#
# install:
# - pip install -U platformio
# - platformio update
#
# script:
# - platformio ci --lib="." --board=ID_1 --board=ID_2 --board=ID_N

3
README.md Normal file
View File

@ -0,0 +1,3 @@
# FREMO Fast Clock
This is a fast clock implementation based on a 2.4GHz wireless transmission layer based on NRF24 controllers.

39
include/readme.txt Normal file
View File

@ -0,0 +1,39 @@
This directory is intended for project header files.
A header file is a file containing C declarations and macro definitions
to be shared between several project source files. You request the use of a
header file in your project source file (C, C++, etc) located in `src` folder
by including it, with the C preprocessing directive `#include'.
```src/main.c
#include "header.h"
int main (void)
{
...
}
```
Including a header file produces the same results as copying the header file
into each source file that needs it. Such copying would be time-consuming
and error-prone. With a header file, the related declarations appear
in only one place. If they need to be changed, they can be changed in one
place, and programs that include the header file will automatically use the
new version when next recompiled. The header file eliminates the labor of
finding and changing all the copies as well as the risk that a failure to
find one copy will result in inconsistencies within a program.
In C, the usual convention is to give header files names that end with `.h'.
It is most portable to use only letters, digits, dashes, and underscores in
header file names, and at most one dot.
Read more about using header files in official GCC documentation:
* Include Syntax
* Include Operation
* Once-Only Headers
* Computed Includes
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html

46
lib/readme.txt Normal file
View File

@ -0,0 +1,46 @@
This directory is intended for project specific (private) libraries.
PlatformIO will compile them to static libraries and link into executable file.
The source code of each library should be placed in a an own separate directory
("lib/your_library_name/[here are source files]").
For example, see a structure of the following two libraries `Foo` and `Bar`:
|--lib
| |
| |--Bar
| | |--docs
| | |--examples
| | |--src
| | |- Bar.c
| | |- Bar.h
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
| |
| |--Foo
| | |- Foo.c
| | |- Foo.h
| |
| |- readme.txt --> THIS FILE
|
|- platformio.ini
|--src
|- main.c
and a contents of `src/main.c`:
```
#include <Foo.h>
#include <Bar.h>
int main (void)
{
...
}
```
PlatformIO Library Dependency Finder will find automatically dependent
libraries scanning project source files.
More information about PlatformIO Library Dependency Finder
- https://docs.platformio.org/page/librarymanager/ldf.html

32
platformio.ini Normal file
View File

@ -0,0 +1,32 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[platformio]
env_default = nanoatmega168
[env:pro8MHzatmega328]
platform = atmelavr
board = pro8MHzatmega328
framework = arduino
lib_deps = ArduinoJson, RadioHead, Adafruit SSD1306, Adafruit_GFX
build_flags = -D WITH_DISPLAY
[env:nanoatmega168]
platform = atmelavr
board = nanoatmega168
framework = arduino
lib_deps = ArduinoJson, RadioHead
[env:nanoatmega328]
platform = atmelavr
board = nanoatmega328
framework = arduino
lib_deps = ArduinoJson, RadioHead, Adafruit SSD1306, Adafruit_GFX
build_flags = -D WITH_DISPLAY

14
src/clockMsg.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef clockMsg_h_included
#define clockMsg_h_included
#include <Arduino.h>
struct clockMsg_s {
uint8_t msgType;
uint8_t hour;
uint8_t minute;
uint8_t second;
};
#define msgType_Clock 'c'
#endif

231
src/main.cpp Normal file
View File

@ -0,0 +1,231 @@
#include <Arduino.h>
#include <SPI.h>
#include <RH_NRF24.h>
#include <RHDatagram.h>
#include "clockMsg.h"
#if WITH_DISPLAY
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define OLED_RESET /*4*/
Adafruit_SSD1306 display(OLED_RESET);
#endif
// Singleton instance of the radio driver
RH_NRF24 nrf24(8, 10); // (CSN, CE)
// RH_NRF24 nrf24(8, 7); // use this to be electrically compatible with Mirf
// RH_NRF24 nrf24(8, 10);// For Leonardo, need explicit SS pin
// RH_NRF24 nrf24(8, 7); // For RFM73 on Anarduino Mini
#define nRF_Channel 1
#define THIS_ADRESS 0 // uint8_t address of this node
// Address RH_BROADCAST_ADDRESS can be used for broadcasts as destination
RHDatagram Datagram(nrf24, THIS_ADRESS);
struct clockMsg_s clockMsg;
int masterConfigPin = 2;
static boolean isMaster = true;
// relays for client's physical clock
int relay1pin = 5;
int relay2pin = 6;
// configs:
int holdRelay_ms = 150;
int minRelayOffTime_ms = 80;
boolean relayActiveLow = true;
struct {
uint8_t hour;
uint8_t minute;
uint8_t second;
} displayedTime;
void updateRelays(struct clockMsg_s currentTime) {
// to move forward for one minute, one of the relays is turned
// on for holdRelay_ms milliseconds, then turned off. Next minute,
// the other relay is turned on for holdRelay_ms.
static long lastChange_ms = 0;
static boolean relay1WasActiveLast = false;
static enum {relayIdle, relayOn, relayOff} relayStatus = relayIdle;
long current_ms = millis();
// Serial.print("currentTime="); Serial.print(currentTime.hour); Serial.print(":"); Serial.print(currentTime.minute); Serial.print(":"); Serial.println(currentTime.second);
if (relayStatus == relayIdle) {
if (!((currentTime.hour % 12) == displayedTime.hour && currentTime.minute == displayedTime.minute)) {
// change updateRelays
digitalWrite(relay1WasActiveLast ? relay2pin : relay1pin, relayActiveLow ? LOW : HIGH);
digitalWrite(relay1WasActiveLast ? relay1pin : relay2pin, relayActiveLow ? HIGH : LOW);
Serial.print("Relay "); Serial.print(relay1WasActiveLast ? 2 : 1); Serial.print(relayActiveLow ? ": LOW, " : ": HIGH, ");
Serial.print("Relay "); Serial.print(relay1WasActiveLast ? 1 : 2); Serial.println(relayActiveLow ? ": HIGH" : ": LOW");
Serial.print("last change: "); Serial.print(lastChange_ms); Serial.print(", current: "); Serial.print(current_ms);
Serial.println(", new relay status: ON");
relay1WasActiveLast = !relay1WasActiveLast;
lastChange_ms = current_ms;
relayStatus = relayOn;
displayedTime.minute++;
if (displayedTime.minute >= 60) {
displayedTime.minute = 0;
displayedTime.hour++;
if (displayedTime.hour >= 12) {
displayedTime.hour = 0;
}
}
Serial.print("displayedTime="); Serial.print(displayedTime.hour); Serial.print(":"); Serial.println(displayedTime.minute);
}
} else if (relayStatus == relayOn && current_ms > lastChange_ms + holdRelay_ms) {
digitalWrite(relay1pin, relayActiveLow ? HIGH : LOW);
digitalWrite(relay2pin, relayActiveLow ? HIGH : LOW);
Serial.print("Relay 1: "); Serial.print(relayActiveLow ? "HIGH, " : "LOW, ");
Serial.print("Relay 2: "); Serial.println(relayActiveLow ? "HIGH" : "LOW");
Serial.print("last change: "); Serial.print(lastChange_ms); Serial.print(", current: "); Serial.print(current_ms);
Serial.println(", new relay status: OFF");
lastChange_ms = current_ms;
relayStatus = relayOff;
} else if (relayStatus == relayOff && current_ms > lastChange_ms + minRelayOffTime_ms) {
Serial.print("last change: "); Serial.print(lastChange_ms); Serial.print(", current: "); Serial.print(current_ms);
Serial.println(", new relay status: IDLE");
lastChange_ms = current_ms;
relayStatus = relayIdle;
}
}
void setup() {
Serial.begin(9600);
while (!Serial)
; // wait for serial port to connect. Needed for Leonardo only
// what is our role?
pinMode(masterConfigPin, INPUT);
pinMode(relay1pin, OUTPUT);
pinMode(relay2pin, OUTPUT);
displayedTime.hour = 0;
displayedTime.minute = 0;
displayedTime.second = 0;
if (!digitalRead(masterConfigPin)) {
isMaster = true;
Serial.println("In Master-Mode");
} else {
isMaster = false;
Serial.println("In Client-Mode");
}
if (!Datagram.init())
Serial.println("Init datagram with nrf24 failed");
// Defaults after init are 2.402 GHz (channel 2), 2Mbps, 0dBm
/*if (!nrf24.setChannel(nRF_Channel))
Serial.println("setChannel failed");
if (!nrf24.setRF(RH_NRF24::DataRate2Mbps, RH_NRF24::TransmitPower0dBm))
Serial.println("setRF failed");
*/
#if WITH_DISPLAY
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.display();
delay(2000);
// Clear the buffer.
display.clearDisplay();
// draw a single pixel
display.drawPixel(10, 10, WHITE);
// Show the display buffer on the hardware.
// NOTE: You _must_ call display after making any drawing commands
// to make them visible on the display hardware!
display.display();
delay(2000);
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(0,0);
display.println("Hello, world!");
#endif
}
void masterLoop()
{
static unsigned long nextTimeTick_ms = millis();
static unsigned long updateEvery_ms = 1000;
static uint8_t hour=0, minute=0, second=0;
if (Datagram.available())
{
// Should be a message for us now
uint8_t buf[RH_NRF24_MAX_MESSAGE_LEN];
uint8_t len = sizeof(buf);
uint8_t from, to, id, flags;
if (Datagram.recvfrom(buf, &len, &from, &to, &id, &flags)) {
Serial.print("got request: ");
Serial.println((char*)buf);
}
else
{
Serial.println("*** Datagram.recvfrom failed");
}
} else {
// prepare clock info
if (nextTimeTick_ms < millis()) {
nextTimeTick_ms += updateEvery_ms;
second++;
if (second >= 60) {
second -= 60;
minute++;
if (minute >= 60) {
minute -= 60;
hour++;
if (hour >= 24) {
hour -= 24;
}
}
}
clockMsg.msgType = msgType_Clock;
clockMsg.hour = hour;
clockMsg.minute = minute;
clockMsg.second = second;
// send clock info as a broadcast message
if (Datagram.sendto((uint8_t *) &clockMsg, sizeof(clockMsg), RH_BROADCAST_ADDRESS)) {
Serial.print(hour); Serial.print(":");
Serial.print(minute); Serial.print(":");
Serial.print(second); Serial.print(" - ");
Serial.println("Sent new clock tick");
}
}
}
}
void clientLoop()
{
// if (nrf24.available())
if (Datagram.available())
{
// Should be a message for us now
uint8_t buf[RH_NRF24_MAX_MESSAGE_LEN];
uint8_t len = sizeof(buf);
uint8_t from, to, id, flags;
if (Datagram.recvfrom(buf, &len, &from, &to, &id, &flags)) {
if (len == sizeof(clockMsg) && buf[0]==msgType_Clock) {
Serial.print("Clock Msg: ");
memcpy(&clockMsg, buf, sizeof(clockMsg));
Serial.print(" h:m:s="); Serial.print(clockMsg.hour); Serial.print(":"); Serial.print(clockMsg.minute); Serial.print(":"); Serial.println(clockMsg.second);
updateRelays(clockMsg);
} else {
Serial.print("got request: ");
Serial.println((char*)buf);
}
}
else
{
Serial.println("*** Datagram.recvfrom failed");
}
}
}
void loop() {
if (isMaster) { masterLoop(); }
else { clientLoop(); }
}