//
// H0 by FreeRTOS kopie poslední odladěné verze 25 s WiFi, NTP, Serials=NEX, I2C, PCF, PCA
// úpravy #ifdef ...
// na zdroje náročná FastLED nahrazena úspornější a jednodušší Adafruit_NeoPixel - funguje dobře
// WIFI a WSLED na core 0
//
// připraveno ISR ale není kód v Task1 PCF
#define DEBUG // Pokud zakomentuju nebo smažu, blok se nepřeloží
#define WIFI // obsluha WiFI - spouštím na jádru 0 podle doporučení
#define TIME // print local time
#define NEX // obsluha next.h
//#define PWM // obsluha pwm_PCA.h // mutexem ošetřit PWM vs PCF na i2c, potkávají se !!!
#define PCF // obsluha PCF.h
#define WSLED // obsluha WSLED - spouštím na jádru 0 podle doporučení AI, wire i2c a NEOPIXEL pak asi nemá kolize
#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
//#include <I2cDiscreteIoExpander.h> // PCF85xx library dočasně kvůli TWI_SUCCESS
//#include <I2cDiscreteIoExpander.h> // PCF85xx library dočasně kvůli TWI_SUCCESS
#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 <Adafruit_NeoPixel.h>
#define NUM_LEDS 32
#define DATA_PIN 15
Adafruit_NeoPixel pixels(NUM_LEDS, DATA_PIN, NEO_GRB + NEO_KHZ800);
#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;
TaskHandle_t Task1; // PCF
TaskHandle_t Task2; // Serial, NEX
TaskHandle_t Task3; // PWM(PCA)
TaskHandle_t Task4; // print local time
TaskHandle_t Task5; // WiFim NTP
TaskHandle_t Task6; // WSLED
int connected = 0;
int tamFila = 200;
#define ISR_pin 32 // PCF chip1 INToutput read
volatile uint8_t interrupt = 0; // true = 1 on interrupt
volatile uint16_t ISR_Xvector;
uint8_t ISRdev = 0x26; // ISR vector device
void IRAM_ATTR doISR(){
//void ARDUINO_ISR_ATTR doISR() {
interrupt = 1;
}
// ------------------------------------ SETUP ------------------------------------------------------------
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
pixels.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)
#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 TIME
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 */
0); /* pin task to core 1 */
//#endif
//#ifdef WSLED
xTaskCreatePinnedToCore(
Task6code, /* Task function. */
"Task6", /* name of task. */
10000, /* Stack size of task */
NULL, /* parameter of the task */
1, /* priority of the task */
&Task6, /* Task handle to keep track of created task */
0); /* pin task to core 1 */
//#endif
//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
attachInterrupt(ISR_pin, doISR, FALLING); // INT signal je aktivni stavem L, vynuluje se prectenim nebo zapisem do portu
// 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
/* 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 PCF running on core ");
Serial.println(xPortGetCoreID());
/// ISR_Xvector = PCF_digitalRead(0,0,ISRdev);
for (;;)
{
if (xSemaphoreTake(xSerialSem2, (TickType_t)5 == pdTRUE)){
Serial.println("Task 1 PCF");
/***********************************************************************************************************/
#ifdef PCF
// PCF readings
if(interrupt == 1) {
Serial.println(" .. interrupted .. ");
ISR_Xvector = PCF_digitalRead(0,0,ISRdev);
Serial.print("ISR_Xvector from isr : ");
Serial.println(ISR_Xvector, BIN); // ISR_Xvector je nacteni z isr
// Serial.println(PCF_digitalRead(1,0x20), BIN);
Serial.println(PCF_digitalRead(0,0,0x21), BIN);
Serial.println(PCF_digitalRead(0,0,0x22), BIN);
Serial.println(PCF_digitalRead(0,0,0x23), BIN);
Serial.println(PCF_digitalRead(0,0,0x24), BIN);
/// pcf_inputs = ~pcf8575_read(i2c_bus, 0x22);
// následující digitalRead teprve schodí int
Serial.print("PCF_digitalRead(0,ISRdev), BIN ");
Serial.println(PCF_digitalRead(0,0,ISRdev), BIN);
interrupt = 0;
// tcaselect(0); // nastavení
/* // puvodni stara verze
i2c_bus = 0; // select system i2c bus (wire())
pcf_inputs = ~pcf8575_read(i2c_bus,0x21); // testování read
Serial.print("bus 0 - 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); // miror, testování write
A_hall = ~pcf8575_read(i2c_bus, 0x22); // ukládám to na zkoušku do uint16_t hall_A a podle toho budu rozsvěcet WSLED
pcf_inputs = ~pcf8575_read(i2c_bus, 0x23);
pcf8575_write(0, 0x23, pcf_inputs); // miror testování write
B_hall = ~pcf8575_read(i2c_bus, 0x23); // ukládám to na zkoušku do uint16_t hall_B a podle toho budu rozsvěcet WSLED
Serial.print("bus 1 - A_hall_22 wire : ");
Serial.println(A_hall,BIN);
*/
interrupt = 0;
} // end of if(interrupt == 1)
#endif PCF
/***********************************************************************************************************/
xSemaphoreGive(xSerialSem2);
} // end of if (xSemaphoreTake(xSerialSem2, (TickType_t)5 == pdTRUE)){
vTaskDelay(100);
//tcaselect(0xFF); // schodí všechny kanály ˇ... >7 )
A_hall = PCF_digitalRead(1,0,0x22); // ukládám to na zkoušku do uint16_t hall_A a podle toho budu rozsvěcet WSLED
//PCF_digitalWrite(0,0,0x22, A_hall); // miror, testování write
Serial.print(" A hall Read ");
//Serial.println(PCF_digitalRead(1,0,0x22), BIN);
Serial.println(A_hall, BIN);
Serial.println(" --- ");
}
}
void Task2code(void *pvParameters) {
Serial.print("Task2 Serials 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 PWM running on core ");
Serial.println(xPortGetCoreID());
#ifdef PWM
PCA_setup();
#endif
for (;;)
{
#ifdef PWM
Serial.println("Task 3 PWM");
write_pwm_blink1();
vTaskDelay(500);
write_pwm_blink2();
vTaskDelay(500);
#endif
//// vTaskDelay(50);
}
}
void Task4code(void *pvParameters) // printLocalTime
{
Serial.print("Task4 local time 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);
}
Serial.println("Task 4 local time");
}
}
void Task5code(void *pvParameters) // WiFi NTP
{
Serial.print("Task5 WIFI NTP 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 WIFI NTP");
}
#endif
vTaskDelay(1000);
}
}
void Task6code(void *pvParameters) // WSLED
{
// asi občas trochu časová kolize ? s i2c ... pořešit Semaforem
// narušuje nejspíš čtení vs zápis od PCF
// v principu to funguje
Serial.print("Task6 WSLED running on core ");
Serial.println(xPortGetCoreID());
#ifdef WSLED
pixels.begin();
pixels.clear(); // Set all pixel colors to 'off'
/* // test času pro 256 WS LED
Serial.print("millis b ");
Serial.println(millis());
for(int i=0; i<256; i++) { // For each pixel...
pixels.setPixelColor(i, pixels.Color(0, 0, 255));
}
pixels.show(); // Send the updated pixel colors to the hardware.
Serial.print("millis e ");
Serial.println(millis()); // trvá to asi 5? ms
delay(1000);
*/
#endif
for (;;)
{
#ifdef WSLED
Serial.println("Task 6 WSLED");
/// Serial.println(millis());
// pixels.clear(); // Set all pixel colors to 'off'
for(int i=0; i<16; i++) { // For each pixel...
A_hall & (1<<i) ? pixels.setPixelColor(i, pixels.Color(0, 255, 0)) : pixels.setPixelColor(i, pixels.Color(255, 0, 0));
}
for(int i=0; i<16; i++) { // For each pixel...
B_hall & (1<<i) ? pixels.setPixelColor(i+16, pixels.Color(0, 255, 0)) : pixels.setPixelColor(i+16, pixels.Color(255, 0, 0));
}
pixels.show(); // Send the updated pixel colors to the hardware.
/// Serial.println(millis()); // trvá to asi 3msec
vTaskDelay(500);
#endif
}
}
70 addr 000
0x20 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
0x26 ISR vect
0x24 100