Initial commit
This commit is contained in:
commit
93a34e2918
|
@ -0,0 +1,6 @@
|
||||||
|
.pio
|
||||||
|
.pioenvs
|
||||||
|
.piolibdeps
|
||||||
|
.clang_complete
|
||||||
|
.gcc-flags.json
|
||||||
|
firmware.bin
|
|
@ -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
|
|
@ -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 --> 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
|
|
@ -0,0 +1,24 @@
|
||||||
|
; 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
|
||||||
|
|
||||||
|
[env:d1_mini]
|
||||||
|
platform = espressif8266
|
||||||
|
board = d1_mini
|
||||||
|
framework = arduino
|
||||||
|
|
||||||
|
lib_deps =
|
||||||
|
# Using a library name
|
||||||
|
MD_Parola
|
||||||
|
MD_MAX72XX
|
||||||
|
IOTAppStory-ESP
|
||||||
|
NTPClient
|
||||||
|
|
||||||
|
upload_port = /dev/cu.wchusbserial1420
|
||||||
|
upload_speed = 921600
|
|
@ -0,0 +1,373 @@
|
||||||
|
/* Wemos8266RelaysLedDisplay/main.cpp
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define COMPDATE __DATE__ __TIME__
|
||||||
|
// Button pin on the esp for selecting modes. 0 for Generic devices!
|
||||||
|
#define MODEBUTTON D3
|
||||||
|
|
||||||
|
#define RELAY1_PIN D1
|
||||||
|
#define RELAY2_PIN D2
|
||||||
|
#define DISPLAY_CLK_PIN D5
|
||||||
|
#define DISPLAY_DATA_PIN D7
|
||||||
|
#define DISPLAY_CS_PIN D6
|
||||||
|
#define VERTICAL_BAR_STARTS_TOP false
|
||||||
|
#define DEBUG_RELAYS false
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <IOTAppStory.h>
|
||||||
|
#include <MD_Parola.h>
|
||||||
|
#include <MD_MAX72xx.h>
|
||||||
|
#include <SPI.h>
|
||||||
|
#include <NTPClient.h>
|
||||||
|
#include <ESP8266WiFi.h>
|
||||||
|
#include <WiFiUdp.h>
|
||||||
|
|
||||||
|
|
||||||
|
IOTAppStory IAS(COMPDATE, MODEBUTTON);
|
||||||
|
String deviceName = "wemosMatrixDisplay";
|
||||||
|
String chipId;
|
||||||
|
const uint16_t WAIT_TIME = 1000;
|
||||||
|
|
||||||
|
// Define the number of devices we have in the chain and the hardware interface
|
||||||
|
// NOTE: These pin numbers will probably not work with your hardware and may
|
||||||
|
// need to be adapted
|
||||||
|
|
||||||
|
/* Mapper result, connector to ESP is at right (backside):
|
||||||
|
Your responses produce these hardware parameters
|
||||||
|
|
||||||
|
HW_DIG_ROWS 1
|
||||||
|
HW_REV_COLS 0
|
||||||
|
HW_REV_ROWS 0
|
||||||
|
|
||||||
|
Your hardware matches the setting for FC-16 modules. Please set FC16_HW.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
|
||||||
|
#define MAX_DEVICES 4
|
||||||
|
#define CLK_PIN DISPLAY_CLK_PIN
|
||||||
|
#define DATA_PIN DISPLAY_DATA_PIN
|
||||||
|
#define CS_PIN DISPLAY_CS_PIN
|
||||||
|
|
||||||
|
// Hardware SPI connection
|
||||||
|
// MD_Parola P = MD_Parola(HARDWARE_TYPE, DISPLAY_CS_PIN, MAX_DEVICES);
|
||||||
|
// Arbitrary output pins
|
||||||
|
MD_Parola P = MD_Parola(HARDWARE_TYPE, DISPLAY_DATA_PIN, DISPLAY_CLK_PIN, DISPLAY_CS_PIN, MAX_DEVICES);
|
||||||
|
|
||||||
|
WiFiUDP ntpUDP;
|
||||||
|
NTPClient timeClient(ntpUDP, "europe.pool.ntp.org", 3600, 60000);
|
||||||
|
|
||||||
|
// Field default values
|
||||||
|
char *clockName = "FastClk ";
|
||||||
|
char *clockSpeed_modelMsPerRealSec_String = "250";
|
||||||
|
int clockSpeed_modelMsPerRealSec = 250;
|
||||||
|
char *relay1Pin_String = "D1";
|
||||||
|
char *relay2Pin_String = "D2";
|
||||||
|
int relay1Pin = D1, relay2Pin = D2;
|
||||||
|
char *relayHoldTime_ms_String = "200";
|
||||||
|
int relayHoldTime_ms = 200;
|
||||||
|
char *relayMinOffTime_ms_String = "100";
|
||||||
|
int relayMinOffTime_ms = 100;
|
||||||
|
|
||||||
|
|
||||||
|
void setupIAS(void) {
|
||||||
|
#if defined ESP8266
|
||||||
|
// creat a unique deviceName for classroom situations (deviceName-123)
|
||||||
|
chipId = String(ESP.getChipId());
|
||||||
|
chipId = "-"+chipId.substring(chipId.length()-3);
|
||||||
|
deviceName += chipId;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// preset deviceName this is also your MDNS responder: http://deviceName.local
|
||||||
|
IAS.preSetDeviceName(deviceName);
|
||||||
|
IAS.preSetAppName(F("Wemos2RelaysMatrixDisplays"));
|
||||||
|
IAS.preSetAppVersion(F("0.0.1"));
|
||||||
|
IAS.preSetAutoUpdate(true);
|
||||||
|
|
||||||
|
// define fields
|
||||||
|
IAS.addField(clockName, "Clock Name", 8, 'T');
|
||||||
|
IAS.addField(clockSpeed_modelMsPerRealSec_String, "Model MilliSec per Real Sec", 8, 'N');
|
||||||
|
IAS.addField(relay1Pin_String, "Pin Relay 1", 2, 'P');
|
||||||
|
IAS.addField(relay2Pin_String, "Pin Relay 2", 2, 'P');
|
||||||
|
IAS.addField(relayHoldTime_ms_String, "Relay hold time (ms)", 3, 'N');
|
||||||
|
IAS.addField(relayMinOffTime_ms_String, "Relay min off time (ms)", 3, 'N');
|
||||||
|
|
||||||
|
IAS.onModeButtonShortPress([]() {
|
||||||
|
Serial.println(F(" If mode button is released, I will enter in firmware update mode."));
|
||||||
|
Serial.println(F("*-------------------------------------------------------------------------*"));
|
||||||
|
P.print("|updt");
|
||||||
|
});
|
||||||
|
|
||||||
|
IAS.onModeButtonLongPress([]() {
|
||||||
|
Serial.println(F(" If mode button is released, I will enter in configuration mode."));
|
||||||
|
Serial.println(F("*-------------------------------------------------------------------------*"));
|
||||||
|
P.print("|cfg");
|
||||||
|
});
|
||||||
|
|
||||||
|
IAS.onFirstBoot([]() {
|
||||||
|
Serial.println(F(" Manual reset necessary after serial upload!"));
|
||||||
|
Serial.println(F("*-------------------------------------------------------------------------*"));
|
||||||
|
P.print("|rst");
|
||||||
|
ESP.reset();
|
||||||
|
});
|
||||||
|
|
||||||
|
IAS.onConfigMode([]() {
|
||||||
|
P.print("WiFi");
|
||||||
|
delay(400);
|
||||||
|
P.print("*" + chipId);
|
||||||
|
Serial.print(F("Entered config mode for Wifi, device=")); Serial.println(chipId);
|
||||||
|
});
|
||||||
|
|
||||||
|
IAS.onFirmwareUpdateCheck([]() {
|
||||||
|
P.print("chk upd");
|
||||||
|
Serial.println(F("Firmware update check"));
|
||||||
|
});
|
||||||
|
|
||||||
|
IAS.onFirmwareUpdateDownload([]() {
|
||||||
|
P.print("dl&instl");
|
||||||
|
Serial.println(F("Download and install new firmware"));
|
||||||
|
});
|
||||||
|
|
||||||
|
IAS.onFirmwareUpdateError([]() {
|
||||||
|
P.print("Err fwu");
|
||||||
|
Serial.println(F("Firmware update error"));
|
||||||
|
});
|
||||||
|
|
||||||
|
// Optional parameter: What to do with EEPROM on First boot of the app?
|
||||||
|
// 'F' Fully erase | 'P' Partial erase(default) | 'L' Leave intact
|
||||||
|
IAS.begin('L');
|
||||||
|
delay(500);
|
||||||
|
// Set to true to enable calling home frequently (disabled by default)
|
||||||
|
IAS.setCallHome(true);
|
||||||
|
// Call home interval in seconds, use 60s only for development.
|
||||||
|
// Please change it to at least 2 hours in production
|
||||||
|
IAS.setCallHomeInterval(120);
|
||||||
|
//IAS.callHome(false /*SPIFFS-check*/);
|
||||||
|
clockSpeed_modelMsPerRealSec = atoi(clockSpeed_modelMsPerRealSec_String);
|
||||||
|
relay1Pin = IAS.dPinConv(relay1Pin_String);
|
||||||
|
relay2Pin = IAS.dPinConv(relay2Pin_String);
|
||||||
|
relayHoldTime_ms = atoi(relayHoldTime_ms_String);
|
||||||
|
relayMinOffTime_ms = atoi(relayMinOffTime_ms_String);
|
||||||
|
|
||||||
|
Serial.println(F("Configuration used:"));
|
||||||
|
Serial.print(F("Relay1 Pin: ")); Serial.println(relay1Pin);
|
||||||
|
Serial.print(F("Relay2 Pin: ")); Serial.println(relay2Pin);
|
||||||
|
Serial.print(F("Clock speed: ")); Serial.print(clockSpeed_modelMsPerRealSec); Serial.println(F(" model ms per real sec"));
|
||||||
|
Serial.print(F("Relay hold time (ms): ")); Serial.println(relayHoldTime_ms);
|
||||||
|
Serial.print(F("Relay min off time (ms): ")); Serial.println(relayMinOffTime_ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setupRelays(int relay1Pin, int relay2Pin) {
|
||||||
|
pinMode(relay1Pin, OUTPUT);
|
||||||
|
pinMode(relay2Pin, OUTPUT);
|
||||||
|
digitalWrite(relay1Pin, LOW);
|
||||||
|
digitalWrite(relay2Pin, LOW);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setupDisplay() {
|
||||||
|
int charCode;
|
||||||
|
#if VERTICAL_BAR_STARTS_TOP
|
||||||
|
static uint8_t verticalBarFont[] = {
|
||||||
|
1, 0x00, /* blank */
|
||||||
|
1, 0x01, /* 1 dot */
|
||||||
|
1, 0x03, /* 2 dots */
|
||||||
|
1, 0x07,
|
||||||
|
1, 0x0f,
|
||||||
|
1, 0x1f,
|
||||||
|
1, 0x3f,
|
||||||
|
1, 0x7f,
|
||||||
|
1, 0xff, /* vertical bar completely set */
|
||||||
|
}; // columns from right to left, each byte is a single column
|
||||||
|
#else
|
||||||
|
static uint8_t verticalBarFont[] = {
|
||||||
|
1, 0x00, /* blank */
|
||||||
|
1, 0x80, /* 1 dot */
|
||||||
|
1, 0xc0, /* 2 dots */
|
||||||
|
1, 0xe0,
|
||||||
|
1, 0xf0,
|
||||||
|
1, 0xf8,
|
||||||
|
1, 0xfc,
|
||||||
|
1, 0xfe,
|
||||||
|
1, 0xff, /* vertical bar completely set */
|
||||||
|
}; // columns from right to left, each byte is a single column
|
||||||
|
#endif
|
||||||
|
|
||||||
|
P.begin();
|
||||||
|
// P.setZoneEffect(0, true, PA_FLIP_LR);
|
||||||
|
P.setIntensity(1);
|
||||||
|
for (charCode=1; charCode<=8; ++charCode) {
|
||||||
|
P.addChar(charCode, verticalBarFont+2*(charCode-1));
|
||||||
|
}
|
||||||
|
char intro[] = {':', '-', ')', ' ', 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x00};
|
||||||
|
P.print(intro);
|
||||||
|
}
|
||||||
|
void setup(void)
|
||||||
|
{
|
||||||
|
Serial.println(F("setup():"));
|
||||||
|
setupDisplay();
|
||||||
|
setupIAS();
|
||||||
|
delay(500);
|
||||||
|
setupRelays(relay1Pin, relay2Pin);
|
||||||
|
delay(500);
|
||||||
|
|
||||||
|
timeClient.begin();
|
||||||
|
Serial.println(F("setup() finished"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool timeClientInitialized = false;
|
||||||
|
static unsigned long lastTimeOutput_ms = 0;
|
||||||
|
#define TIME_BETWEEN_TIME_REPORTS_ms 60000
|
||||||
|
|
||||||
|
static unsigned long last_relay_off_ts=0, last_relay_hold_ts=0;
|
||||||
|
enum RelayState { RELAY_STATE_OFF=0, RELAY_STATE_ON_EVEN_MINUTE, RELAY_STATE_ON_ODD_MINUTE };
|
||||||
|
static RelayState relaysState = RELAY_STATE_OFF;
|
||||||
|
static RelayState lastRelayOnState = RELAY_STATE_ON_EVEN_MINUTE;
|
||||||
|
static bool relayCanSwitch=true;
|
||||||
|
|
||||||
|
void toggleRelays() {
|
||||||
|
if (relayCanSwitch) {
|
||||||
|
if (lastRelayOnState == RELAY_STATE_ON_EVEN_MINUTE) {
|
||||||
|
digitalWrite(relay1Pin, HIGH);
|
||||||
|
digitalWrite(relay2Pin, LOW);
|
||||||
|
relaysState = RELAY_STATE_ON_ODD_MINUTE;
|
||||||
|
// P.print("R-OEv");
|
||||||
|
} else {
|
||||||
|
digitalWrite(relay1Pin, LOW);
|
||||||
|
digitalWrite(relay2Pin, HIGH);
|
||||||
|
relaysState = RELAY_STATE_ON_EVEN_MINUTE;
|
||||||
|
// P.print("R-OOd");
|
||||||
|
}
|
||||||
|
lastRelayOnState = relaysState;
|
||||||
|
} // else P.print("R-OErr");
|
||||||
|
relayCanSwitch = false;
|
||||||
|
last_relay_hold_ts = millis();
|
||||||
|
Serial.println(F("Toggle Relays"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void relaysOff(void) {
|
||||||
|
digitalWrite(relay1Pin, LOW);
|
||||||
|
digitalWrite(relay2Pin, LOW);
|
||||||
|
last_relay_off_ts = millis();
|
||||||
|
relaysState = RELAY_STATE_OFF;
|
||||||
|
// P.print("R-Off");
|
||||||
|
}
|
||||||
|
|
||||||
|
void loopRelays(void) {
|
||||||
|
if (relaysState == RELAY_STATE_OFF) {
|
||||||
|
if (millis() - last_relay_off_ts > relayMinOffTime_ms) {
|
||||||
|
relayCanSwitch = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (millis() - last_relay_hold_ts > relayHoldTime_ms) {
|
||||||
|
relaysOff();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void loop(void)
|
||||||
|
{
|
||||||
|
int currentDisplayState;
|
||||||
|
int hours, minutes, seconds;
|
||||||
|
char minuteProgressIndicator;
|
||||||
|
static int lastMinutes = 0;
|
||||||
|
static int lastSeconds = 0;
|
||||||
|
static char timeBuffer[10];
|
||||||
|
#define MsgSize 10
|
||||||
|
static char debugMsg[MsgSize+1];
|
||||||
|
static int recentDisplayState = -1;
|
||||||
|
|
||||||
|
if (!timeClientInitialized && WiFi.status() == WL_CONNECTED) {
|
||||||
|
timeClient.begin();
|
||||||
|
timeClientInitialized = true;
|
||||||
|
}
|
||||||
|
IAS.loop();
|
||||||
|
|
||||||
|
if (timeClientInitialized && millis()-lastTimeOutput_ms > TIME_BETWEEN_TIME_REPORTS_ms) {
|
||||||
|
timeClient.update();
|
||||||
|
Serial.println(timeClient.getFormattedTime());
|
||||||
|
lastTimeOutput_ms = millis();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timeClientInitialized) {
|
||||||
|
hours = timeClient.getHours();
|
||||||
|
minutes = timeClient.getMinutes();
|
||||||
|
seconds = timeClient.getSeconds();
|
||||||
|
} else {
|
||||||
|
hours = (millis() / 60 * 60 * 1000) % 24;
|
||||||
|
minutes = (millis() / 60 * 1000) % 60;
|
||||||
|
seconds = (millis() / 1000) % 60;
|
||||||
|
}
|
||||||
|
minuteProgressIndicator = seconds/7.5 + 1; // char code 1-8 show vertical bar
|
||||||
|
snprintf(timeBuffer, 10, "%c %02d:%02d", minuteProgressIndicator, hours, minutes);
|
||||||
|
|
||||||
|
/* DEBUG */
|
||||||
|
#if DEBUG_RELAYS
|
||||||
|
if (seconds != lastSeconds) {
|
||||||
|
switch (seconds % 4) {
|
||||||
|
case 0:
|
||||||
|
digitalWrite(relay1Pin, HIGH);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
digitalWrite(relay1Pin, LOW);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
digitalWrite(relay2Pin, HIGH);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
digitalWrite(relay2Pin, LOW);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Serial.print("Rel dbg: "); Serial.println(seconds, HEX);
|
||||||
|
delay(5);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
/* END DEBUG */
|
||||||
|
|
||||||
|
currentDisplayState = seconds / 5; // Value 0..11
|
||||||
|
|
||||||
|
static bool executed = false;
|
||||||
|
if (recentDisplayState != currentDisplayState) executed = false;
|
||||||
|
|
||||||
|
#define ExecOnce(p) {if (!executed) {executed=true; p;}}
|
||||||
|
|
||||||
|
switch (currentDisplayState) {
|
||||||
|
case 0: ExecOnce(P.print(timeBuffer)); break;
|
||||||
|
case 1:
|
||||||
|
snprintf(debugMsg, MsgSize, "%c t%cr%c", minuteProgressIndicator, timeClientInitialized ? 'x':'-', (relaysState == RELAY_STATE_OFF)?'-':(relaysState == RELAY_STATE_ON_EVEN_MINUTE?'e':'o'));
|
||||||
|
ExecOnce(P.print(debugMsg));
|
||||||
|
break;
|
||||||
|
case 2: ExecOnce(P.print(timeBuffer)); break;
|
||||||
|
case 3: // if (lastSeconds != seconds) P.print(seconds); break;
|
||||||
|
case 4: ExecOnce(P.print(timeBuffer)); break;
|
||||||
|
case 5: ExecOnce(P.print(timeBuffer)); break;
|
||||||
|
case 6:
|
||||||
|
switch (minutes % 3) {
|
||||||
|
case 0: snprintf(debugMsg, MsgSize, "%c s%d", minuteProgressIndicator, clockSpeed_modelMsPerRealSec); break;
|
||||||
|
case 1: snprintf(debugMsg, MsgSize, "%c h%d", minuteProgressIndicator, relayHoldTime_ms); break;
|
||||||
|
case 2: snprintf(debugMsg, MsgSize, "%c o%d", minuteProgressIndicator, relayMinOffTime_ms); break;
|
||||||
|
}
|
||||||
|
ExecOnce(P.print(debugMsg));
|
||||||
|
break;
|
||||||
|
case 7: ExecOnce(P.print(timeBuffer)); break;
|
||||||
|
case 8:
|
||||||
|
/*snprintf(debugMsg, MsgSize, "t%cr%c", timeClientInitialized ? 'x':'-', (relaysState == RELAY_STATE_OFF)?'-':(relaysState == RELAY_STATE_ON_EVEN_MINUTE?'e':'o'));
|
||||||
|
ExecOnce(P.print(debugMsg));
|
||||||
|
break;*/
|
||||||
|
case 9: ExecOnce(P.print(timeBuffer)); break;
|
||||||
|
case 10: if (lastSeconds != seconds) P.printf("%c %d", minuteProgressIndicator, seconds); break;
|
||||||
|
case 11: ExecOnce(P.print(timeBuffer)); break;
|
||||||
|
default: ExecOnce(P.print("default")); break;
|
||||||
|
}
|
||||||
|
recentDisplayState = currentDisplayState;
|
||||||
|
|
||||||
|
// toggle relays
|
||||||
|
if (lastMinutes != minutes) {
|
||||||
|
toggleRelays();
|
||||||
|
lastMinutes = minutes;
|
||||||
|
}
|
||||||
|
lastSeconds = seconds;
|
||||||
|
|
||||||
|
loopRelays();
|
||||||
|
}
|
|
@ -0,0 +1,236 @@
|
||||||
|
|
||||||
|
|
||||||
|
// Test software to map display hardware rows and columns
|
||||||
|
// Generic SPI interface and only one MAX72xx/8x8 LED module required
|
||||||
|
//
|
||||||
|
// Does not use any libraries as the code is used to directly map the display orientation
|
||||||
|
// Observe the display and relate it to the MAX7219 hardware being exercised through the
|
||||||
|
// instructions and output on the serial monitor.
|
||||||
|
//
|
||||||
|
// NOTE: You need to change the hardware pins to match your specific setup
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <SPI.h>
|
||||||
|
|
||||||
|
#define SERIAL_SPEED 115200
|
||||||
|
// Hardware definition
|
||||||
|
#define CLK_PIN D5 // or SCK
|
||||||
|
#define DATA_PIN D7 // or MOSI
|
||||||
|
#define CS_PIN D6 // or SS
|
||||||
|
|
||||||
|
// Opcodes for the MAX7221 and MAX7219
|
||||||
|
// All OP_DIGITn are offsets from OP_DIGIT0
|
||||||
|
#define OP_NOOP 0 ///< MAX72xx opcode for NO OP
|
||||||
|
#define OP_DIGIT0 1 ///< MAX72xx opcode for DIGIT0
|
||||||
|
#define OP_DIGIT1 2 ///< MAX72xx opcode for DIGIT1
|
||||||
|
#define OP_DIGIT2 3 ///< MAX72xx opcode for DIGIT2
|
||||||
|
#define OP_DIGIT3 4 ///< MAX72xx opcode for DIGIT3
|
||||||
|
#define OP_DIGIT4 5 ///< MAX72xx opcode for DIGIT4
|
||||||
|
#define OP_DIGIT5 6 ///< MAX72xx opcode for DIGIT5
|
||||||
|
#define OP_DIGIT6 7 ///< MAX72xx opcode for DIGIT6
|
||||||
|
#define OP_DIGIT7 8 ///< MAX72xx opcode for DIGIT7
|
||||||
|
#define OP_DECODEMODE 9 ///< MAX72xx opcode for DECODE MODE
|
||||||
|
#define OP_INTENSITY 10 ///< MAX72xx opcode for SET INTENSITY
|
||||||
|
#define OP_SCANLIMIT 11 ///< MAX72xx opcode for SCAN LIMIT
|
||||||
|
#define OP_SHUTDOWN 12 ///< MAX72xx opcode for SHUT DOWN
|
||||||
|
#define OP_DISPLAYTEST 15 ///< MAX72xx opcode for DISPLAY TEST
|
||||||
|
|
||||||
|
#define MAX_DIG 8
|
||||||
|
#define MAX_SEG 8
|
||||||
|
|
||||||
|
#define USER_DELAY 1000 // ms
|
||||||
|
|
||||||
|
void spiTransmit(uint8_t opCode, uint8_t data)
|
||||||
|
{
|
||||||
|
// enable the devices to receive data
|
||||||
|
digitalWrite(CS_PIN, LOW);
|
||||||
|
|
||||||
|
// shift out the data
|
||||||
|
shiftOut(DATA_PIN, CLK_PIN, MSBFIRST, opCode);
|
||||||
|
shiftOut(DATA_PIN, CLK_PIN, MSBFIRST, data);
|
||||||
|
|
||||||
|
// latch the data onto the display
|
||||||
|
digitalWrite(CS_PIN, HIGH);
|
||||||
|
}
|
||||||
|
|
||||||
|
void instructions(void)
|
||||||
|
{
|
||||||
|
Serial.print(F("\nINTRODUCTION\n------------"));
|
||||||
|
Serial.print(F("\nHow the LED matrix is wired is important for the MD_MAX72xx library as different"));
|
||||||
|
Serial.print(F("\nLED modules are wired differently. The library can accommodate these, but it"));
|
||||||
|
Serial.print(F("\nneeds to know what transformations need to be carried out to map your board to the"));
|
||||||
|
Serial.print(F("\nstandard coordinate system. This utility shows you how the matrix is wired so that"));
|
||||||
|
Serial.print(F("\nyou can set the correct *_HW module type for your application."));
|
||||||
|
Serial.print(F("\n\nThe standard functions in the library expect that:"));
|
||||||
|
Serial.print(F("\no COLUMNS are addressed through the SEGMENT selection lines, and"));
|
||||||
|
Serial.print(F("\no ROWS are addressed through the DIGIT selection lines."));
|
||||||
|
Serial.print(F("\n\nThe DISPLAY always has its origin in the top right corner of a display:"));
|
||||||
|
Serial.print(F("\no LED matrix module numbers increase from right to left,"));
|
||||||
|
Serial.print(F("\no Column numbers (ie, the hardware segment numbers) increase from right to left (0..7), and "));
|
||||||
|
Serial.print(F("\no Row numbers (ie, the hardware digit numbers) increase down (0..7)."));
|
||||||
|
Serial.print(F("\n\nThere are three hardware setting that describe your hardware configuration:"));
|
||||||
|
Serial.print(F("\n- HW_DIG_ROWS - HardWare DIGits are ROWS. This will be 1 if the digits map to the rows"));
|
||||||
|
Serial.print(F("\n of the matrix, 0 otherwise"));
|
||||||
|
Serial.print(F("\n- HW_REV_COLS - HardWare REVerse COLumnS. The normal column coordinates orientation"));
|
||||||
|
Serial.print(F("\n is col 0 on the right side of the display. This will be 1 if reversed."));
|
||||||
|
Serial.print(F("\n (ie, hardware 0 is on the left)."));
|
||||||
|
Serial.print(F("\n- HW_REV_ROWS - HardWare REVerse ROWS. The normal row coordinates orientation is row"));
|
||||||
|
Serial.print(F("\n 0 at top of the display. This will be 1 if reversed (ie, row 0"));
|
||||||
|
Serial.print(F("\n is at the bottom)."));
|
||||||
|
Serial.print(F("\n\nThese individual setting then determine the model type of the hardware you are using."));
|
||||||
|
Serial.print(F("\n\nINSTRUCTIONS\n------------"));
|
||||||
|
Serial.print(F("\n1. Wire up one matrix only, or cover up the other modules, to avoid confusion."));
|
||||||
|
Serial.print(F("\n2. Enter the answers to the question in the edit field at the top of Serial Monitor."));
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup(void)
|
||||||
|
{
|
||||||
|
Serial.begin(SERIAL_SPEED);
|
||||||
|
Serial.print(F("\n\n[MD_MAX72xx Hardware mapping utility]\n"));
|
||||||
|
instructions();
|
||||||
|
|
||||||
|
// Initialize comms hardware
|
||||||
|
digitalWrite(CS_PIN, HIGH);
|
||||||
|
pinMode(CS_PIN, OUTPUT);
|
||||||
|
pinMode(DATA_PIN, OUTPUT);
|
||||||
|
pinMode(CLK_PIN, OUTPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void initialize(void)
|
||||||
|
// Initialize the display devices.
|
||||||
|
// On initial power-up, all control registers are reset, the
|
||||||
|
// display is blanked, and the MAX7219/MAX7221 enters shutdown
|
||||||
|
// mode.
|
||||||
|
{
|
||||||
|
spiTransmit(OP_SHUTDOWN, 1); // wake up
|
||||||
|
spiTransmit(OP_SCANLIMIT, 7); // all on
|
||||||
|
spiTransmit(OP_INTENSITY, 7); // mid intensity
|
||||||
|
spiTransmit(OP_DISPLAYTEST, 0); // no test
|
||||||
|
spiTransmit(OP_DECODEMODE, 0); // no decode
|
||||||
|
}
|
||||||
|
|
||||||
|
void mapSegment(char *label, uint8_t data)
|
||||||
|
{
|
||||||
|
Serial.print(F("-"));
|
||||||
|
Serial.print(label);
|
||||||
|
spiTransmit(OP_DIGIT0, data);
|
||||||
|
delay(USER_DELAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
void mapDigit(uint8_t opCode)
|
||||||
|
{
|
||||||
|
Serial.print(F("-"));
|
||||||
|
Serial.print(opCode - OP_DIGIT0);
|
||||||
|
spiTransmit(opCode, 0xff);
|
||||||
|
delay(USER_DELAY);
|
||||||
|
spiTransmit(opCode, 0x0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear(void)
|
||||||
|
{
|
||||||
|
for (uint8_t i=0; i<MAX_DIG; i++)
|
||||||
|
spiTransmit(OP_DIGIT0 + i, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
char getResponse(char *validInput)
|
||||||
|
// blocking wait for user input from the serial monitor
|
||||||
|
{
|
||||||
|
char c = '\0';
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (Serial.available())
|
||||||
|
{
|
||||||
|
uint8_t i;
|
||||||
|
|
||||||
|
c = Serial.read();
|
||||||
|
for (i=0; validInput[i] != '\0' && validInput[i] != c; i++)
|
||||||
|
; // set the index I to the matching character or nul if none - all work done in the loop
|
||||||
|
c = validInput[i]; // could be nul character
|
||||||
|
}
|
||||||
|
} while (c == '\0');
|
||||||
|
|
||||||
|
Serial.print(c);
|
||||||
|
|
||||||
|
return(toupper(c));
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
boolean def_dig_rows, def_rev_cols, def_rev_rows;
|
||||||
|
|
||||||
|
clear();
|
||||||
|
|
||||||
|
Serial.print(F("\n\n======================================================"));
|
||||||
|
Serial.print(F("\n\nSTEP 1 - DIGITS MAPPING (rows)\n------------------------------"));
|
||||||
|
Serial.print(F("\nIn this step you will see a line moving across the LED matrix."));
|
||||||
|
Serial.print(F("\nYou need to observe whether the bar is scanning ROWS or COLUMNS,"));
|
||||||
|
Serial.print(F("\nand the direction it is moving."));
|
||||||
|
Serial.print(F("\n>> Enter Y when you are ready to start: "));
|
||||||
|
getResponse("Yy");
|
||||||
|
|
||||||
|
initialize();
|
||||||
|
|
||||||
|
Serial.print("\nDig");
|
||||||
|
for (uint8_t i=0; i<MAX_DIG; i++)
|
||||||
|
mapDigit(OP_DIGIT0+i);
|
||||||
|
|
||||||
|
clear();
|
||||||
|
|
||||||
|
Serial.print(F("\n>> Enter Y if you saw ROWS animated, N if you saw COLUMNS animated: "));
|
||||||
|
def_dig_rows = (getResponse("YyNn") == 'Y');
|
||||||
|
|
||||||
|
if (def_dig_rows)
|
||||||
|
Serial.print(F("\n>> Enter Y if you saw the line moving BOTTOM to TOP, or enter N otherwise: "));
|
||||||
|
else
|
||||||
|
Serial.print(F("\n>> Enter Y if you saw the line moving LEFT to RIGHT, or enter N otherwise: "));
|
||||||
|
def_rev_rows = (getResponse("YyNn") == 'Y');
|
||||||
|
|
||||||
|
Serial.print(F("\n\nSTEP 2 - SEGMENT MAPPING (columns)\n----------------------------------"));
|
||||||
|
Serial.print(F("\nIn this step you will see a dot moving along one edge of the LED matrix."));
|
||||||
|
Serial.print(F("\nYou need to observe the direction it is moving."));
|
||||||
|
Serial.print(F("\n>> Enter Y when you are ready to start: "));
|
||||||
|
getResponse ("Yy");
|
||||||
|
|
||||||
|
Serial.print(F("\nSeg"));
|
||||||
|
mapSegment("G", 1);
|
||||||
|
mapSegment("F", 2);
|
||||||
|
mapSegment("E", 4);
|
||||||
|
mapSegment("D", 8);
|
||||||
|
mapSegment("C", 16);
|
||||||
|
mapSegment("B", 32);
|
||||||
|
mapSegment("A", 64);
|
||||||
|
mapSegment("DP", 128);
|
||||||
|
|
||||||
|
clear();
|
||||||
|
|
||||||
|
if (def_dig_rows)
|
||||||
|
Serial.print(F("\n>> Enter Y if you saw the LED moving LEFT to RIGHT, or enter N otherwise: "));
|
||||||
|
else
|
||||||
|
Serial.print(F("\n>> Enter Y if you saw the LED moving BOTTOM to TOP, or enter N otherwise: "));
|
||||||
|
def_rev_cols = (getResponse("YyNn") == 'Y');
|
||||||
|
|
||||||
|
Serial.print(F("\n\nSTEP 3 - RESULTS\n----------------"));
|
||||||
|
Serial.print(F("\nYour responses produce these hardware parameters\n"));
|
||||||
|
Serial.print(F("\nHW_DIG_ROWS\t")); Serial.print(def_dig_rows ? 1 : 0 );
|
||||||
|
Serial.print(F("\nHW_REV_COLS\t")); Serial.print(def_rev_cols ? 1 : 0 );
|
||||||
|
Serial.print(F("\nHW_REV_ROWS\t")); Serial.print(def_rev_rows ? 1 : 0 );
|
||||||
|
|
||||||
|
Serial.print(F("\n\nYour hardware matches the setting for "));
|
||||||
|
if (def_dig_rows && def_rev_cols && !def_rev_rows)
|
||||||
|
Serial.print(F("Parola modules. Please set PAROLA_HW."));
|
||||||
|
else if (!def_dig_rows && def_rev_cols && !def_rev_rows)
|
||||||
|
Serial.print(F("Generic modules. Please set GENERIC_HW."));
|
||||||
|
else if (def_dig_rows && def_rev_cols && def_rev_rows)
|
||||||
|
Serial.print(F("IC Station modules. Please set ICSTATION_HW."));
|
||||||
|
else if (def_dig_rows && !def_rev_cols && !def_rev_rows)
|
||||||
|
Serial.print(F("FC-16 modules. Please set FC16_HW."));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Serial.print(F("none of the preconfigured module types."));
|
||||||
|
Serial.print(F("\nYou should try rotating the matrix by 180 degrees and re-running this utility."));
|
||||||
|
Serial.print(F("\n\nIf that still fails to provide a solution - congratulations! You have discovered"));
|
||||||
|
Serial.print(F("\na new type of hardware module! Please contact the author of the libraries so that"));
|
||||||
|
Serial.print(F("\nthese can be included in the next official release."));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
|
||||||
|
This directory is intended for PIO Unit Testing and project tests.
|
||||||
|
|
||||||
|
Unit Testing is a software testing method by which individual units of
|
||||||
|
source code, sets of one or more MCU program modules together with associated
|
||||||
|
control data, usage procedures, and operating procedures, are tested to
|
||||||
|
determine whether they are fit for use. Unit testing finds problems early
|
||||||
|
in the development cycle.
|
||||||
|
|
||||||
|
More information about PIO Unit Testing:
|
||||||
|
- https://docs.platformio.org/page/plus/unit-testing.html
|
Loading…
Reference in New Issue