Ik ben opzoek naar een manier om een lus S88 modules uit te lezen met een Arduino of ESP32.
/* A program to accept occupancy detector status from the Canbus and set the proper bit in the S88 shift register Copyright (C) Meino de Graaf, all rights reserved This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. email: meino@innerside.demon.nl*/#include <CanBus.h>#define DEBUG 0//// Pin layout is for the Arduino Uno//// Connections for S88 bus://// s88 pin 1 Data - Arduino pin 8 = Data_Out to Oommand Station, or pin 7 (Data in) from the previous Arduino in the chain// s88 pin 2 GND - Arduino GND// s88 pin 3 Clock - Arduino pin 2, interrupt 0// s88 pin 4 PS - Arduino pin 3, interrupt 1// S88 pin 5 Reset (not used)// s88 pin 6 V+ - Arduino 5V (not used)//// Connections for S88N (RJ45)//// RJ45 pin 1 12V/5V (not used)// RJ45 pin 2 Data - Arduino pin 8 = Data_Out to Oommand Station, or pin 7 (Data in) from the previous Arduino in the chain// RJ45 pin 3 GND - Arduino GND// RJ45 pin 4 Clock - Arduino pin 2, interrupt 0// RJ45 pin 5 GND - Arduino GND// RJ45 pin 6 PS - Arduino pin 3, interrupt 1// RJ45 pin 7 Reset (not used)// RJ45 pin 8 Raildata (not used)//#define S88_DATAOUT_PIN 8#define S88_DATAIN_PIN 7#define S88_CLOCK_PIN 2#define S88_PS_PIN 3#define S88_CLOCK_INTERRUPT 0#define S88_PS_INTERRUPT 1const int nrOfOccDetectors = 6 * 16;#if (DEBUG)typedef struct{ char *description;} CDT; // Structure describing an attached Current Detector//// We have implemented here 5 banks of S88 occupancy detectors//volatile CDT occDetector[nrOfOccDetectors] = { {"S4"}, // 1.01 {"S6-2"}, // 1.02 {"S11-1"}, // 1.03 {"S5"}, // 1.04 {"S19A (W4+W5)"}, // 1.05 {"S11-2"}, // 1.06 {"S8-1"}, // 1.07 {"S6-1"}, // 1.08 {"S1"}, // 1.09 {"S2-1"}, // 1.10 {"S3A"}, // 1.11 {"W8+W9"}, // 1.12 {"S8"}, // 1.13 {"S9-1"}, // 1.14 {"S10A-1"}, // 1.15 {"S7A"}, // 1.16 {"S5-1"}, // 2.01 {"S1-1"}, // 2.02 {"S12-1 (W6)"}, // 2.03 {"S12-2 (W7)"}, // 2.04 {"S19-2"}, // 2.05 {"S19-1"}, // 2.06 {"S2-1 (W12)"}, // 2.07 {"S9-2"}, // 2.08 {"S4A"}, // 2.09 {"S10A-2"}, // 2.10 {"S3A-1"}, // 2.11 {"S4-1"}, // 2.12 {"S7A-1"}, // 2.13 {"W1"}, // 2.14 {"W2+W3"}, // 2.15 {"W10+W11"}, // 2.16 {"S10B-1"}, // 3.01 {"S10B-2"}, // 3.02 {"S10C-1"}, // 3.03 {"S10C-2"}, // 3.04 {"S10D-1"}, // 3.05 {"S10D-2"}, // 3.06 {"S7B+S7C (LSe)"}, // 3.07 {"S3B+S3C (LSe)"}, // 3.08 {"S3B+S3C (LSb)"}, // 3.09 {"S7B+S7C (LSb)"}, // 3.10 {"not used"}, // 3.11 {"not used"}, // 3.12 {"not used"}, // 3.13 {"not used"}, // 3.14 {"not used"}, // 3.15 {"not used"}, // 3.16 {"TO-01"}, // 4.01 {"TO-02"}, // 4.02 {"TO-03"}, // 4.03 {"TO-04"}, // 4.04 {"TO-05"}, // 4.05 {"TO-06"}, // 4.06 {"TO-07"}, // 4.07 {"TO-08"}, // 4.08 {"TO-09"}, // 4.09 {"TO-10"}, // 4.10 {"TO-11"}, // 4.11 {"TO-12"}, // 4.12 {"TO-13"}, // 4.13 {"TO-14"}, // 4.14 {"TO-15"}, // 4.15 {"TO-16"}, // 4.16 {"TO-17"}, // 5.01 {"TO-18"}, // 5.02 {"TO-19"}, // 5.03 {"TO-20"}, // 5.04 {"TO-21"}, // 5.05 {"TO-22"}, // 5.06 {"TO-23"}, // 5.07 {"TO-24"}, // 5.08 {"TO-25"}, // 5.09 {"TO-26"}, // 5.10 {"not used"}, // 5.11 {"not used"}, // 5.12 {"not used"}, // 5.13 {"not used"}, // 5.14 {"not used"}, // 5.15 {"not used"}, // 5.16 {"B-14"}, // 6.01 {"B-15"}, // 6.02 {"B-16"}, // 6.03 {"B-12"}, // 6.04 {"W-4"}, // 6.05 {"W-5"}, // 6.06 {"W-6"}, // 6.07 {"W-7"}, // 6.08 {"W-8"}, // 6.09 {"W-8"}, // 6.10 {"not used"}, // 6.11 {"not used"}, // 6.12 {"not used"}, // 6.13 {"not used"}, // 6.14 {"not used"}, // 6.15 {"not used"} // 6.16};#endif //DEBUGvolatile byte pendingStates[nrOfOccDetectors];volatile byte dataRegister[nrOfOccDetectors];volatile long clockTicks = 0;CanBus *canBus;void setup(){#if (DEBUG || CANBUS_DEBUG || CANBUS_DEBUG1) Serial.begin(115200); while (!Serial); Serial.println("Starting S88 interface"); Serial.flush();#endif //DEBUG // // Intialize the dataRegister // memset(pendingStates,0,nrOfOccDetectors); memset(dataRegister,0,nrOfOccDetectors);#if (DEBUG) Serial.println("Setup 1"); Serial.flush();#endif // // Setup the input/output pins and the interrupts for the S88 communication // //pinMode(S88_CLOCK_PIN, INPUT_PULLUP); //pinMode(S88_PS_PIN, INPUT_PULLUP); pinMode(S88_DATAIN_PIN, INPUT_PULLUP); pinMode(S88_DATAOUT_PIN, OUTPUT); digitalWrite(S88_DATAOUT_PIN, LOW); attachInterrupt(S88_CLOCK_INTERRUPT, clock_Int, RISING); attachInterrupt(S88_PS_INTERRUPT, PS_Int, RISING);#if (DEBUG) Serial.println("Setup 2"); Serial.flush();#endif // // Setup and start the CanBus communication // canBus = new CanBus(S88_INTERFACE_ID, nullptr, new Mcp2515(10)); // Only a receiver canBus->setMsgReceiver(OCC_DT, occDtReceiver);#if (DEBUG) Serial.println("Setup 3"); Serial.flush();#endif canBus->begin();#if (DEBUG) Serial.println("Setup done"); Serial.flush();#endif}void loop(){ // // Are there messages on the CanBus? // canBus->heartBeat();}//// A function that executes whenever a message is received from the Canbus// It's purpose is to analyze the received event Occupancy detector and// update the proper element in the semaphore table or turnout table.//void occDtReceiver(unsigned char aMsgLen, OCC_DT_MSG *msgData){ if (msgData->address < nrOfOccDetectors) { pendingStates[msgData->address] = msgData->state; #if (DEBUG) Serial.print("[occDtReceiver] "); Serial.print("occId: "); Serial.print(occDetector[msgData->address].description); Serial.print(" adress: "); Serial.print(msgData->address); Serial.print(" bank: "); Serial.print(((msgData->address)/16)+1); Serial.print(" detector: "); Serial.print(((msgData->address)%16)+1); Serial.print(", occState: "); if (pendingStates[msgData->address] > 0) Serial.println("OCCUPIED"); else Serial.println("FREE");#endif //DEBUG } else {#if (DEBUG) Serial.print("[occDtReceiver] invalid occId: "); Serial.println(msgData->address);#endif }}void PS_Int(){ // // Load the detector states in the data register // memcpy(dataRegister,pendingStates,nrOfOccDetectors); clockTicks = 0; // reset index in the dataRegister}//// Set the state of the next detector on the output pin//void clock_Int(){ digitalWrite(S88_DATAOUT_PIN, dataRegister[clockTicks]); // // If we are chained to additional detectors, reads a bit from the chain and // store it in the current, just written, position // // Delay makes reading output signal from next Arduino in chain more reliable. //delayMicroseconds(2); //dataRegister[clockTicks]=digitalRead(S88_DATAIN_PIN); clockTicks = (clockTicks + 1) % nrOfOccDetectors; // Goto next bit in dataRegister}
Gewoon 1 print waar alleen nog de Nano op hoeft.Hoe haalbaar denk je dat dit is?
Al kan ik het prima lezen en aanpassen natuurlijk.
Nu lijkt het mij mooi om bijvoorbeeld via PCBWay 1 printje te maken die jouw DCC-in (noem ik het maar even) combineert met de PCA9685.En dan met insteek headers waar je een Arduino Nano op kan prikken. En mocht dat nodig zijn ook al met buckconverter erop.
En daarna via een simpele wizard kan installeren zonder ingewikkelde stappen
Daar een standaard Arduino Nano (of iets vergelijkbaars) op kan klikken
Mijn oplossing ziet er zo uit: DCC aanluiting, 10 servo-aansluitmogelijkheden, 5V voedingsaansluiting en 3 knopjes waarmee de servo-standen eenmalig ingesteld kunnen worden. Een volgende versie heeft de servo-aansluitingen per 3 en 4 met wat tussenruimte gegroepeerd, omdat 10 servo-stekkertjes strak naast elkaar niet echt fijn is.
Het grootste voordeel is dat je maar 2 draden hoeft aan te sluiten en je hoeft niet al die headers te solderen daar je gewoon dat IO shield kan blijven gebruiken. Dit is mijn idee van 'minimalisme'.
Ik zou die PCA echt vergeten, echt onnodig. 12 servo's direct op de arduino werkt prima.
Het makkelijkste wat ik in dit opzicht ooit bereikt heb, was een zipje samenstellen waarin o.a. avrdude.exe zit (het programma waarmee je .hex bestanden in een arduino flasht) en een .hex (= programma voor arduino) en een .bat script. De .hex in de arduino krijgen was zo simpel als dubbelklikken op upload.bat.
Ik heb zelf ook een 'filosofie' omtrent spoorse zaken. Ik geloof in een 1 bus systeem zoals BiDib of loconet (B) waarbij je schakeldecoders en terugmelders op dezelfde bus kan aansluiten.
Kan ook met netwerkkabels aan elkaar verbonden worden... helemaal makkelijk en in alle lengtes verkrijgbaar.
Ik zou uiteindelijk de voorkeur hebben aan iets wat bijvoorbeeld via PCBWay besteld kan worden en al helemaal gesoldeerd is. (volgens mij kan je daar prints met componenten gesoldeerd bestellen).
Heb je deze print zelf ontworpen? Of is deze ergens te koop?En heb je daar ook een sketch bij voor?
#include "src/servoSweep.h"#include "src/NmraDCC.h"NmraDcc Dcc ; // declare DCC objectconst int myAddress = 20 ;const int nServo = 4 ;ServoSweep servo[nServo] = // declare servo array with 4 elements{ ServoSweep( 3, 45, 135, 1, 10 ) ,// pin, min, max, turn off after turning, 10ms interval between degrees. ServoSweep( 4, 45, 135, 1, 10 ) , ServoSweep( 5, 45, 135, 1, 10 ) , ServoSweep( 6, 45, 135, 1, 10 /* relay pin optional*/) ,} ;void notifyDccAccTurnoutOutput( uint16_t Addr, uint8_t Direction, uint8_t OutputPower ) // called from DCC library when a turnout is set{ if( OutputPower == 0 ) return ; // often you receive 2 times the same packet, this is ment to control the pulse length for coil drives. We don't use this. for( int i = 0 ; i < nServos ; i ++ ) { if( Addr == myAddress + i ) // check if recveived address matches with any of my 4 servo's in this case 20-23 { servo[i].setState( Direction ) ; // set one of the servo's in a direction } }}void setup(){ Dcc.pin(0, DCC_PIN, 1); Dcc.init( MAN_ID_DIY, 10, CV29_ACCESSORY_DECODER | CV29_OUTPUT_ADDRESS_MODE, 0 ); for( int i = 0 ; i < nServos ; i ++ ) { servo[i].begin() ; }}void loop(){ Dcc.process(); for( int i = 0 ; i < nServos ; i ++ ) { servo[i].sweep() ; // keep updating all servo motors }}
Deze print heb ik zelf ontworpen met EasyEDA (https://easyeda.com/nl Std Edition) Dit werkt erg prettig samen met JLCPCB, zeker als je eventueel ook met componenten wilt laten bestucken, dan is het vrij makkelijk om de componenten uit het "basic" assortiment te kiezen.Een sketch heb ik ook, maar die is niet geschikt om te delen. Ik ben geen (nette) programmeur. Ik heb gewoon hier en daar wat stukjes code gekopieerd en samengeplakt
Dit zou geweldig zijn... schakeldecoders en terugmelders, alles hetzelfde... en dan zou mijn voorkeur uitgaan naar het TCP protocol.Dan werkt elke schakeldecoder en terugmelder als een PC in een LAN netwerk.Kan ook met netwerkkabels aan elkaar verbonden worden... helemaal makkelijk en in alle lengtes verkrijgbaar.