//
// H0 by FreeRTOS kopie poslední odladěné verze 25 s WiFi, NTP, Serials=NEX, I2C, PCF, PCA
//
//#define DEBUG // Pokud zakomentuju nebo smažu, blok se nepřeloží
#define WIFI // obsluha WiFI
/////#define NTP // obsluha NTP.h
#define NEX // obsluha next.h
#define PWM // obsluha pwm_PCA.h
#define PCF // obsluha PCF.h
//#define WSLED // to už překračuje limit free verze 2MB i při ostatních def vypnutých
#include "defs.h" // deklarace společných proměnných
#include <Wire.h> // arduino knihovna i2c
#include <LiquidCrystal_I2C.h> // arduino knihovna lcd
#include "I2C.h" // podprogramy pro konunikaci po I2C
#ifdef WIFI
#include <WiFi.h>
#endif
//#ifdef NTP // NTP vždy pokud WIFI
//#include "NTP.h"
//#endif
#define NTP_SERVER "pool.ntp.org"
#define UTC_OFFSET 0
#define UTC_OFFSET_DST 0
#define timeout 5000 // 5 seconds WiFi connection timeout
#ifdef NEX
#include "NEX_routines.h"
uint8_t NEX_m_length; // length of available new message form Serial2
#endif
//byte dataBuffer[16];
#ifdef WSLED
#include <FastLED.h>
#define NUM_LEDS 16
#define DATA_PIN 15
CRGB leds[NUM_LEDS];
#endif
#ifdef PWM
#include "PCA_routines.h"
#endif
#ifdef PCF
#include "PCF_routines.h"
uint16_t pcf_inputs;
#endif
//uint8_t error;
unsigned long duration, period, start, finished, elapsed; // pomocne pro time
uint8_t j;
QueueHandle_t xFila;
SemaphoreHandle_t xSerialSem;
SemaphoreHandle_t xSerialSem2;
#ifdef PCF
TaskHandle_t Task1; // PCF
#endif
#ifdef NEX
TaskHandle_t Task2; // Serial, NEX
#endif
#ifdef PWM
TaskHandle_t Task3; // PWM(PCA)
#endif
TaskHandle_t Task4; // print local time
#ifdef WIFI
TaskHandle_t Task5; // WiFim NTP
#endif
int connected = 0;
int tamFila = 200;
void setup() {
Serial.begin(115200); // hwUART0 default pins RX=GPIO3 TX=GPIO1, can be remapped
Serial.println("Hello, H0 by ESP32!");
// Serial.println(F(__FILE__ " " __DATE__ " " __TIME__)); //File compiler details
//Serial1.begin(9600); // hwUART1 e.g. pins RX=GPI09 TX=GPI10, flash memory conflict..
Serial.setTimeout(100); // Set timeout todefault is 1 seconds
#ifdef NEX
Serial2.begin(9600, SERIAL_8N1, 16, 17); // hwUART2 e.g. pins RX=GPI16 TX=GPI17 for Nextion serial
Serial2.setTimeout(50); // Set timeout to miliseconds - v simulaci nejlíp 50msec
#endif NEX
#ifdef WSLED
FastLED.addLeds<WS2812, DATA_PIN, GRB>(leds, NUM_LEDS); // GRB ordering is typical
#endif
/*
#ifdef PWM
PCA_setup();
#endif
*/
if (xSerialSem == NULL)
{
xSerialSem = xSemaphoreCreateMutex();
if (xSerialSem != NULL)
xSemaphoreGive(xSerialSem);
}
if (xSerialSem2 == NULL)
{
xSerialSem2 = xSemaphoreCreateMutex();
if (xSerialSem2 != NULL)
xSemaphoreGive(xSerialSem2);
}
xFila = xQueueCreate( tamFila, sizeof( String ) );
if(xFila == NULL){
Serial.println("Erro criando fila");
}
// put your setup code here, to run once:
lcd.init();
lcd.backlight();
lcd.clear();
lcd.setCursor(0, 0);
lcd.println("H0 Railway ESP32");
lcd.setCursor(0, 1);
lcd.println("freeRTOS v: 21");
// best safe use hwUART2: pins RX=GPI16/TX=GPI17 independent off usb programming,boot/loop,internal memory, wifi etc
Wire.begin(); // běží s default parametry
Wire.setClock(Wire_FREQ);
// Wire.begin(SDA, SCL, Wire_FREQ); // možnost změny default parametrů
// !!! Wire1.begin(); // nefunguje bez zadání pinů
Wire1.begin(SDA_1, SCL_1, Wire_1_FREQ); // druhá sběrnice, deklarace parametrů v I2C.h //
// Serial.println("Hello, H0 by ESP32!");
// vTaskDelay(pdMS_TO_TICKS(500));
// scan i2c proběhne v setupu před spuštěním tasků
// I2C_scan(0); // scan systemové sběrice Wire()
// I2C_scan(1); // scan sběrnice Wire1()
//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
#ifdef PCF
xTaskCreatePinnedToCore(
Task1code, /* Task function. */
"Task1", /* name of task. */
10000, /* Stack size of task */
NULL, /* parameter of the task */
1, /* priority of the task */
&Task1, /* Task handle to keep track of created task */
1); /* pin task to core 0 */
#endif
#ifdef NEX
xTaskCreatePinnedToCore(
Task2code, /* Task function. */
"Task2", /* name of task. */
10000, /* Stack size of task */
NULL, /* parameter of the task */
1, /* priority of the task */
&Task2, /* Task handle to keep track of created task */
1); /* pin task to core 1 */
#endif
#ifdef PWM
xTaskCreatePinnedToCore(
Task3code, /* Task function. */
"Task3", /* name of task. */
10000, /* Stack size of task */
NULL, /* parameter of the task */
1, /* priority of the task */
&Task3, /* Task handle to keep track of created task */
1); /* pin task to core 1 */
#endif
//#ifdef xxx
xTaskCreatePinnedToCore(
Task4code, /* Task function. */
"Task4", /* name of task. */
10000, /* Stack size of task */
NULL, /* parameter of the task */
1, /* priority of the task */
&Task4, /* Task handle to keep track of created task */
1); /* pin task to core 1 */
//#endif
#ifdef WIFI
xTaskCreatePinnedToCore(
Task5code, /* Task function. */
"Task5", /* name of task. */
10000, /* Stack size of task */
NULL, /* parameter of the task */
1, /* priority of the task */
&Task5, /* Task handle to keep track of created task */
1); /* pin task to core 1 */
#endif
//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
// vTaskDelay(pdMS_TO_TICKS(500));
Serial.println("verze Serial2.readstring, timeout 50ms ještě potřeba dodělat parsing");
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////// main loop //////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void loop() {
delay(10); // this speeds up the simulation
#ifdef WSLED
leds[0] = CRGB::Orange;
leds[1] = CRGB::Green;
leds[2] = CRGB::Red;
leds[3] = CRGB::Black;
//...
leds[6] = CRGB::Blue;
leds[7] = CRGB::White;
FastLED.show();
vTaskDelay(500);
leds[0] = CRGB::Orange;
leds[1] = CRGB::Black;
leds[2] = CRGB::Black;
leds[3] = CRGB::Orange;
//...
leds[6] = CRGB::White;
leds[7] = CRGB::Blue;
FastLED.show();
vTaskDelay(500);
#endif
/* zkouška práce se struct
A_track_control[8].direction_s = 63;
Serial.println(A_track_control[8].direction_s);
*/
#ifdef NEX
// nex_read_data();
/* if (dataReady) { // dataReady deklarovano v NEX_routines
nex_parse_data();
} */
dataReady = false;
//Serial2.print("j0.val=25");
//sendTermination();
#endif NEX
#ifdef PWM
// Serial.println(millis());
// write_pwm(); // trvá cca
// write_pwm_blink();
// Serial.println(millis());
#endif
// vTaskDelay(pdMS_TO_TICKS(1000));
/* zkouška práce se struct
A_track_control[8].direction_s = 63;
Serial.println(A_track_control[8].direction_s);
*/
}
/*******************************************************************************************************************/
void spinner() {
static int8_t counter = 0;
const char* glyphs = "\xa1\xa5\xdb";
lcd.setCursor(15, 1);
lcd.print(glyphs[counter++]);
if (counter == strlen(glyphs)) {
counter = 0;
}
}
void setTimezone(String timezone){
Serial.printf(" Setting Timezone to %s\n",timezone.c_str());
setenv("TZ",timezone.c_str(),1); // Now adjust the TZ. Clock settings are adjusted to show the new local time
tzset();
}
// setting the internal ESP32 RTC
void setTime(int yr, int month, int mday, int hr, int minute, int sec, int isDst){
struct tm tm;
tm.tm_year = yr - 1900; // Set date
tm.tm_mon = month-1;
tm.tm_mday = mday;
tm.tm_hour = hr; // Set time
tm.tm_min = minute;
tm.tm_sec = sec;
tm.tm_isdst = isDst; // 1 or 0
time_t t = mktime(&tm);
Serial.printf("Setting time: %s", asctime(&tm));
struct timeval now = { .tv_sec = t };
settimeofday(&now, NULL);
}
void printLocalTime_F()
{
struct tm timeinfo;
if (!getLocalTime(&timeinfo)){
lcd.setCursor(0, 1);
lcd.println("Connection Err");
connected = 0;
return;
}
// lcd.clear();
// lcd.setCursor(0, 0);
// lcd.println(" ");
lcd.setCursor(8, 0);
lcd.println(&timeinfo, "%H:%M:%S");
lcd.setCursor(0, 1);
lcd.println(&timeinfo, "%d/%m/%Y %Z");
}
//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
void Task1code(void *pvParameters) {
Serial.print("Task1 running on core ");
Serial.println(xPortGetCoreID());
for (;;)
{
if (xSemaphoreTake(xSerialSem2, (TickType_t)5 == pdTRUE)){
Serial.println("Task 1");
#ifdef PCF
// PCF readings
i2c_bus = 0; // select system i2c bus (wire())
pcf_inputs = ~pcf8575_read(i2c_bus,0x21); // testování read
Serial.println("PCF8575_21 wire : ");
Serial.println(pcf_inputs,BIN);
i2c_bus = 1; // select (wire1) i2c bus
tcaselect(0); // přepínání kanálů funguje, TCA na sběrnici 1 (Wire1())
pcf_inputs = ~pcf8575_read(i2c_bus, 0x22);
pcf8575_write(0, 0x22, pcf_inputs); // testování write
pcf_inputs = ~pcf8575_read(i2c_bus, 0x23);
pcf8575_write(0, 0x23, pcf_inputs); // testování write
#endif PCF
xSemaphoreGive(xSerialSem2);
}
vTaskDelay(100);
}
}
void Task2code(void *pvParameters) {
Serial.print("Task2 running on core ");
Serial.println(xPortGetCoreID());
for (;;)
{
#ifdef NEX
// Serial.println("Task 2");
String dataBuffer = "";
// Kontrola, zda jsou v bufferu vůbec nějaká data k dispozici
if (Serial.available() > 0) {
// Načte text ze sériové linky, dokud nenarazí na odřádkování ('\n') // to ale NEX neposílá
String dataBuffer = Serial.readStringUntil('ÿ'); // ukončí při prvním znaku 0xFF,
//ale z NEX jdou ještě další dva 0xFF které načte ve dvou čtec.cyklech jako prázdné message
dataBuffer.trim(); // Odstraní případné neviditelné znaky na konci (např. '\r' při CR+LF)
m_length = dataBuffer.length();
Serial.print("Received from Serial ");
Serial.print(m_length);
Serial.println(" byte, indexed output :");
for(uint8_t index = 0; index < m_length+1; index++)
{
Serial.print(dataBuffer[index]); // mohu pracovat s jednotlivými byte pole
}
Serial.println("");
Serial.print("dataBuffer : ");
Serial.println(dataBuffer);
Serial.println(".....");
Serial2.print(dataBuffer);
Serial2.write(0xFF);
Serial2.write(0xFF);
Serial2.write(0xFF);
}
if (Serial2.available() > 0) {
dataBuffer = Serial2.readString();
if(dataBuffer.length()>0) {
// if(dataBuffer.length()>0 && dataBuffer.startsWith("e",0) && dataBuffer.endsWith("ÿÿÿ")) {
if(dataBuffer.startsWith("e",0)) Serial.println("starts with e");
if(dataBuffer.endsWith("ÿÿÿ")) Serial.println("ends with ÿÿÿ");
/// if((dataBuffer.substring(1) == 'ÿÿÿ')) Serial.println("dataBuffer.substring(1) == ÿÿÿ"); // nějak NEFUNGUJE
if(dataBuffer.substring(4) == "ÿÿÿ") Serial.println("dataBuffer.substring(4) == ÿ"); // nějak NEFUNGUJE
Serial.print("dataBuffer : ");
Serial.println(dataBuffer);
}
if(dataBuffer.length()>0)
{
NEX_touch = dataBuffer[0]; // Touch Event
if(NEX_touch == 0x65){
NEX_page = dataBuffer[1]; // Touch Event
NEX_id = dataBuffer[2]; // Attribute id
NEX_value = dataBuffer[3]; // Attribute value
Serial.print("Code : 0x");
Serial.println(NEX_touch,HEX);
Serial.print("Page : ");
Serial.println(NEX_page);
Serial.print("Component : ");
Serial.println(NEX_id);
Serial.print("Value : ");
Serial.println(NEX_value);
}
else{
Serial.print("Received from Serial2 ");
for(uint8_t index = 0; index < m_length; index++)
{
Serial.print(":");
Serial.print(dataBuffer[index],HEX); // mohu pracovat s jednotlivými byte pole, HEXa výpis
}
Serial.println("");
Serial.print("dataBuffer : ");
Serial.println(dataBuffer);
Serial.println("");
// Serial.println(" <<");
}
Serial.println("");
}
}
vTaskDelay(50); // čím delší tím méně stíhá zparcovat následující telegram a načte 2 i více do datBufferu
// zkusit si pohrát se semafory a ? queue frontou ? - pak ale jiné vyhodnocení stringu dataBuffer
// a nebo nechat a bude ignorovat příliš rychlé povely z NEX
// možná to "zdržujou" i Serial.print() prozatimních výpisů;
// povely budu ukládat zrychleně do příslušných struct....
#endif
}
}
void Task3code(void *pvParameters)
{
Serial.print("Task3 running on core ");
Serial.println(xPortGetCoreID());
#ifdef PWM
PCA_setup();
#endif
for (;;)
{
#ifdef PWM
Serial.println("Task 3");
write_pwm_blink1();
vTaskDelay(500);
write_pwm_blink2();
vTaskDelay(500);
#endif
}
}
void Task4code(void *pvParameters) // printLocalTime
{
Serial.print("Task4 running on core ");
Serial.println(xPortGetCoreID());
// Serial.println("Temp: " + String(dht.readTemperature(), 1) + " \260C");
// Serial.println("Hum: " + String(dht.readHumidity(), 1) + " %");
for (;;)
{
if( connected == 1){
if (xSemaphoreTake(xSerialSem, (TickType_t)5 == pdTRUE)){
/*
if (xSemaphoreTake(xSerialSem2, (TickType_t)10 == pdTRUE)){
xSemaphoreGive(xSerialSem2);
}
*/
printLocalTime_F();
vTaskDelay(1000);
xSemaphoreGive(xSerialSem);
}
}else{
vTaskDelay(500);
}
}
}
void Task5code(void *pvParameters) // WiFi NTP
{
Serial.print("Task5 running on core ");
Serial.println(xPortGetCoreID());
for (;;)
{
#ifdef WIFI
if( connected == 0){
if (xSemaphoreTake(xSerialSem, (TickType_t)5 == pdTRUE)){
Serial.println("Task 5 - connecting WiFi");
lcd.setCursor(0, 0);
lcd.print("Connecting to ");
lcd.setCursor(0, 1);
lcd.print("WiFi ");
WiFi.begin("Wokwi-GUEST", "", 6); // channel 6
while (WiFi.status() != WL_CONNECTED)
{
vTaskDelay(10);
spinner();
}
Serial.println("");
Serial.println("WiFi connected");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
lcd.clear();
lcd.setCursor(0, 0);
// lcd.println("FreeRTOS H0_21");
lcd.println("Online ");
lcd.setCursor(0, 1);
lcd.println("Updating time...");
connected = 1;
configTime(UTC_OFFSET, UTC_OFFSET_DST, NTP_SERVER);
setTimezone("CET-1CEST,M3.5.0,M10.5.0/3");
xSemaphoreGive(xSerialSem);
Serial.print("Status: ");
Serial.println(connected);
}
Serial.println("Task 5");
}
#endif
vTaskDelay(1000);
}
}
70 addr 000
0 addr 000
LCD 0x27
0x21 001
0x22 010
0x42 00010
0x43 00011
pwm1 0x41 - 00001
0x23 011
0x22 010
0x23 011
0x42 00010
0x43 00011
pwm1 0x41 - 00001
0x21 001