You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

796 lines
21 KiB

/*
* ZMHW Project: Map
*
* A device to give you a visual representation of what Monitors
* are active in your camera network.
*
* SteakElectronics, December 2018
* Rare steak, well done electronics.
*
* Based on: telnet client
*
* Requires: UIPEthernet library
*
* Nice to haves in future:
* PWM dimming, after alarms are off, so you know what was recently on.
* Blinking on active alarms, instead of solid light, maybe.
* Faster direct port access than digitalwrite. There are some libraries
* but I don't know if they support the Mega. Instead, I'm going to rebuild the board
* with a led matrix, and then use pwm pins, and I will deal with it then.
* EDIT: instead maybre I'll use two 0402 resistors, and make a voltage divider, to allow for
* dimmer led, with hardware instead of software.
*
* Troubleshooting Programming: When connecting, it can take 30-60 seconds
* to speed this up, run the test suite telnet connect and it should connect
* immediately after. The idea is that, it seems to connect as soon as a packet is
* sent from zmtrigger. So send a test telnet packet, then it will connect after.
*/
/*
Telnet client
This sketch connects to a a telnet server (http://www.google.com)
using an Arduino Wiznet Ethernet shield. You'll need a telnet server
to test this with.
Processing's ChatServer example (part of the network library) works well,
running on port 10002. It can be found as part of the examples
in the Processing application, available at
http://processing.org/
Circuit:
* Ethernet shield attached to pins 10, 11, 12, 13
created 14 Sep 2010
modified 9 Apr 2012
by Tom Igoe
*/
#include <SPI.h>
#include <UIPEthernet.h>
#define DEBUGMODE 0 // 1:on 0:off
#define DEBUGMODE2 0
// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[] = {
0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0x41
};
IPAddress ip(192, 168, 78, 60);
// Enter the IP address of the server you're connecting to:
IPAddress server(192, 168, 78, 123);
// Port of ZMTrigger
#define ZMTRIGGERPORT 6802
// Initialize the Ethernet client library
// with the IP address and port of the server
// that you want to connect to (port 23 is default for telnet;
// if you're using Processing's ChatServer, use port 10002):
EthernetClient client;
//Make a server
//Not currently working.
#define LISTENPORT 80 // (port 80 is default for HTTP)
EthernetServer server2 = EthernetServer(LISTENPORT);
//Globals
char* zmtrigarray[50] = {0};
uint8_t x = 0;
uint8_t y = 0;
uint8_t Mon1 = 0;
uint8_t Mon2 = 0;
uint8_t MonFIN = 0;
char mask = 0x0F; //get last nibble
uint8_t Mon1Nib = Mon1 & mask;
uint8_t Mon2Nib = Mon2 & mask;
char on = 0b01101110; //n (ascii)
char off = 0b01100110; //f (ascii)
int value = 0;
int ADD = 3; //We start at digital pin 4. so add 3 to everything.
//e.g. Monitor 3 is digital pin 6.
uint8_t NEEDCOLOUR = 0;
// Simple PWM
//if this is 1, bypass interrupt / softpwm, else do softpwm
uint8_t BypassSoftPWM[50] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
int ledState[50] = {0}; // ledState used to set the LED
#define LEDTIME 10000 // 2000 comes out to about 8 seconds
uint16_t onInterval[50] = {LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME,LEDTIME}; // How long to display dimmed led
uint8_t FLIP[50] = {0};
uint8_t PWMCounter = 0;
uint8_t OffOverride[50] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
uint8_t OffCtr[50] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
//during interrupt, we SoftPWM any LEDs that are ready to be done so
ISR(TIMER3_COMPA_vect) {
for(x=1;x<51;x++){
if(BypassSoftPWM[x] == 1){
//Serial.print("Bypass activated for monitor: ");
//Serial.println(x);
///FLIP[x] = 0;
if(onInterval[x] == LEDTIME){
//note: These serial prints, cause delays that mess up softpwm
if(DEBUGMODE){
Serial.print(F("Turning on SoftPWM for Monitor: "));
Serial.println(x);
}
}
//turn off
//might be better to do equals for all cases
if(PWMCounter < 7){
digitalWrite(x + ADD, 0);
}
//turn on
//must be after turn off, otherwise, if before, it will
//immediately be turned off
if(PWMCounter > 7){
digitalWrite(x + ADD, 1);
PWMCounter = 0;
//Serial.print(F("Dim Led #: "));
//Serial.println(x);
}
PWMCounter = PWMCounter + 1;
//Serial.print("PWM Count is: ");
//Serial.println(PWMCounter);
//This is only for debug. Slows delay down.
//Serial.print("oninterval is:");
//Serial.println(onInterval[x]);
onInterval[x] = onInterval[x] - 1;
if (onInterval[x] == 0){
BypassSoftPWM[x] = 0;
//note: These serial prints, cause delays that mess up softpwm
if(DEBUGMODE){
Serial.print("Turning off softPWM on Monitor: ");
Serial.println(x);
}
digitalWrite(x + ADD, 0);
onInterval[x] = LEDTIME;
//no need for softpwm override
OffOverride[x] = 0;
OffCtr[x] = 0;
}
}
}
}
//Check if any monitors that didn't get the shutdown message
ISR(TIMER4_COMPA_vect) {
for(x=1;x<51;x++){
if (OffOverride[x] == 1){
OffCtr[x]++;
if (OffCtr[x] == 50){
OffOverride[x] = 0;
OffCtr[x] = 0;
digitalWrite(x + ADD, 0);
Serial.print(F("Turning off due to override. Monitor #: "));
Serial.println(x);
sendPixel(0,10,10);
}
}
}
}
void setup() {
// in case we are restarting the setup loop because we didn't connect,
// we don't want timer to interfere
TCCR3A = 0;
TCCR3B = 0;
TCNT3 = 0;
//RGB LED
/*define Pin 2/PE4 as output*/
/*Libraries included w/arduino IDE*/
DDRE = 0b00010000;
// Open serial communications and wait for port to open:
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
sendPixel(10,0,0);
delay(500);
sendPixel(0,10,0);
delay(500);
sendPixel(0,0,10);
Serial.println("ZMHW Project: Map");
//Using pins 4 to 49
for (x=4;x<49;x++){
pinMode(x, OUTPUT);
digitalWrite(x, LOW);
}
// start the Ethernet connection:
Ethernet.begin(mac, ip);
// give the Ethernet shield a second to initialize:
delay(1000);
Serial.println("connecting...");
// if you get a connection, report back via serial:
if (client.connect(server, ZMTRIGGERPORT)) {
Serial.println("connected");
//Test all LEDs
for (x=4;x<49;x++){
digitalWrite(x, HIGH);
delay(50);
digitalWrite(x, LOW);
}
} else {
// if you didn't get a connection to the server:
Serial.println("connection failed");
for(int err = 0; err<3;err++){
sendPixel(0,10,0);
delay(100);
sendPixel(0,0,0);
delay(100);
}
}
// AVR Timer CTC Interrupts Calculator
// v. 8
// http://www.arduinoslovakia.eu/application/timer-calculator
// Microcontroller: ATmega2560
// Created: 2019-02-02T06:08:33.492Z
//dimming is buggy. doesn't quite work.
//problem is viewed with a scope, the timers seem to fault
//only pwming on one led occasionally
//setupTimer3();
sendPixel(0,10,10);
setupTimer4();
}
void loop() {
// if there are incoming bytes available
// from the server, read them and print them:
//original
/*if (client.available()) {
char c = client.read();
Serial.print(c);
}*/
//if (client.available()) {
// char* zmtrigarray = client.read();
// Serial.print(zmtrigarray);
//}
if (client.available()){
for(int x=0;x<40;x++){
//there are two UIPClient.ccp client.read functions. this is the 2nd
zmtrigarray[x] = client.read();
if(zmtrigarray[x] == 0b00001010){ //if line break is found in telnet
y = x;
goto PRINT;
}
}
PRINT:
uint8_t WhereInTheWorldIsThe1stLine = 0;
uint8_t WhereInTheWorldIsThe2ndLine = 0;
//Serial.println("Finished reading client");
if(DEBUGMODE){
for(int x=0;x<y;x++){
Serial.print((char)zmtrigarray[x]);
}
Serial.println(F(""));
//debugging
for(int x=0;x<y;x++){
Serial.print(F("x is: "));
Serial.print(x);
Serial.print(F(" val is: "));
Serial.print((char)zmtrigarray[x]);
Serial.print(F(" bytecode is: "));
Serial.print((char)zmtrigarray[x], DEC); //print int rep of byte
Serial.print(F(" binary is: "));
Serial.print((char)zmtrigarray[x], BIN); //print int rep of byte
//line break is 10 in telnet
Serial.println(F(""));
}
Serial.println(F(""));
}//end DEBUG
//Find out monitor we have
//
if(zmtrigarray[1] == 0b01111100) { //looking for vertical dash, in spot 2 (1 of array)
Mon1 = zmtrigarray[0];
Mon1Nib = Mon1 & mask;
MonFIN = Mon1Nib;
WhereInTheWorldIsThe1stLine = 1;
if(DEBUGMODE){
Serial.print(F("Mon1 is: "));
Serial.println(Mon1Nib, BIN);
Serial.print(F("Monitor number is: "));
Serial.println(MonFIN, DEC);
Serial.println(MonFIN, BIN);
}
}
if(zmtrigarray[1] != 0b01111100){ //looking for vertical dash, for two digit number
Mon1 = zmtrigarray[0];
Mon1Nib = Mon1 & mask; //convert from ascii to decimal
WhereInTheWorldIsThe1stLine = 2;
if(DEBUGMODE){
Serial.print(F("Mon1 is: "));
Serial.println(Mon1Nib, BIN);
}
Mon2 = zmtrigarray[1];
Mon2Nib = Mon2 & mask; //convert from ascii to decimal
if(DEBUGMODE){
Serial.print(F("Mon2 is: "));
Serial.println(Mon2Nib, BIN);
}
MonFIN = Mon1Nib & Mon2Nib;
char MonBUF[2];
sprintf(MonBUF,"%d%d", Mon1Nib,Mon2Nib);
MonFIN = atoi(MonBUF); //To convert from Ascii to dec, we take the lower 4 bits,
//convert to a string, then convert to int.
if(DEBUGMODE){
Serial.print(F("Monitor number is: "));
Serial.println(MonFIN, DEC);
Serial.println(MonFIN, BIN);
}
}//end vertical dash search (when in spot 3)
//Looking for an N or an F (for ON or OFF)
//possible values: On, Off
char OnOffSwitch = 0;
OnOffSwitch = zmtrigarray[WhereInTheWorldIsThe1stLine+2];
if(DEBUGMODE){
Serial.print(F("On/Off Switch is: "));
Serial.println(OnOffSwitch);
}
if(OnOffSwitch == on){
//Now, we block SoftPWM, in case LED was dimmed
BypassSoftPWM[MonFIN] = 0;
//Reset this, in case LED was dimmed
onInterval[MonFIN] = LEDTIME;
OffOverride[MonFIN] = 0;
OffCtr[MonFIN] = 0;
if (DEBUGMODE){
Serial.print("Bypass soft pwm value is:");
Serial.println(BypassSoftPWM[MonFIN], DEC);
Serial.print(F("On Detected for Monitor: "));
Serial.println(MonFIN, DEC);
}
value = (MonFIN + ADD);
//minus 1 as array starts at 0
//BypassSoftPWM[MonFIN - 1] = 1;
//Serial.print("Bypass soft pwm value is:");
//Serial.println(BypassSoftPWM[MonFIN - 1], DEC);
LEDAlight(value, 0xFF);
//Visual notification alarm has been off lately
sendPixel(20,20,20);
}
else if(OnOffSwitch == off){
if (DEBUGMODE){
Serial.print(F("Off Detected for Monitor: "));
Serial.println(MonFIN, DEC);
}
value = (MonFIN + ADD);
//Start watchdog timer to shutdown leds that dont stop
OffOverride[MonFIN] = 1;
//Now, we allow SoftPWM, as LED was just turned off.
//minus 1 as array starts at 0
BypassSoftPWM[MonFIN] = 1;
if (DEBUGMODE){
Serial.print(F("Bypass soft pwm value is:"));
Serial.println(BypassSoftPWM[MonFIN], DEC);
}
LEDAlight(value, 0x00); //turn led off
//SoftPWM_LED_dim(value, Interval, MonFIN);
}
else if (OnOffSwitch != on && OnOffSwitch != off){
Serial.println(F("Incorrect On/Off Value"));
}
}
//WebServer(); //todo
// as long as there are bytes in the serial queue,
// read them and send them out the socket if it's open:
while (Serial.available() > 0) {
char inChar = Serial.read();
if (client.connected()) {
client.print(inChar);
}
}
// if the server's disconnected, stop the client:
if (!client.connected()) {
Serial.println();
Serial.println("disconnecting.");
client.stop();
// do nothing:
//while (true);
//GREEN to RED Error
for(int err = 0; err<9;err++){
sendPixel(10,0,0);
delay(100);
sendPixel(0,10,0);
delay(100);
}
//start over
setup();
}
}
uint8_t LEDAlight (int Monitor, int Switch){
//Enable or Disable GPIO based on Monitor
//and Switch value
digitalWrite(Monitor, Switch);
if (DEBUGMODE){
Serial.print(F("LED # has been switched: "));
Serial.print(Monitor);
Serial.print(F(" "));
}
if(Switch == 0xFF){
if (DEBUGMODE){
Serial.println(F(" On"));
}
}
else{
if (DEBUGMODE){
Serial.println(F(" Off"));
}
}
}
//This will fade out the LED
uint8_t SoftPWM_LED_dim (uint8_t Monbitbangpwm, uint16_t *ptr, uint8_t ptrarraynum){
//arrays start at 0
ptrarraynum = ptrarraynum - 1;
if(DEBUGMODE){
//check I did my pointers right...
Serial.println(F("SoftPWM: ...."));
Serial.print(F("Mega Pin is:"));
Serial.println(Monbitbangpwm);
int TempMon = Monbitbangpwm - ADD;
Serial.print(F("Monitor is:"));
Serial.println(TempMon);
Serial.print(F("Interval from within softPWM is:"));
//It's easier to understand, if you call it pass reference to pointer, not pass by reference to pointer.
//aka pointer can pass value (the value of the data, but not the original data), or pass reference (a reference to the original data)
//that little "by" word, makes things much more incomprehensible.
for( int x = 0 ; x <= 50 ; x++ ){
Serial.println( ptr[x], HEX );
//ptr[x] = ptr[x] + 1; //looks like by default arrays pass a reference to array
//no need for ptr magic
// https://forum.arduino.cc/index.php?topic=42546.0
}
}
if(DEBUGMODE)
{
Serial.println( ptr[ptrarraynum], DEC );
//Now start LED dimming timer
//ptr[ptrarraynum] = 200;
//Serial.println( "now 200: ");
//Serial.println( ptr[ptrarraynum], DEC );
}
//start LED dimming timer
ptr[ptrarraynum] = 2000;
Serial.println( ptr[ptrarraynum], DEC );
Serial.println( ptrarraynum, DEC );
Serial.println( "Is now at what was above the num.");
}
//interrupt 15hz
//from https://www.arduinoslovakia.eu/application/timer-calculator
//would be nice if it was downloadable...
void setupTimer3() {
noInterrupts();
// Clear registers
TCCR3A = 0;
TCCR3B = 0;
TCNT3 = 0;
/*
// 15.00060002400096 Hz (16000000/((16665+1)*64))
OCR3A = 16665;
// CTC
TCCR3B |= (1 << WGM32);
// Prescaler 64
TCCR3B |= (1 << CS31) | (1 << CS30);
// Output Compare Match A Interrupt Enable
TIMSK3 |= (1 << OCIE3A);
*/
/*
// 60.00060000600006 Hz (16000000/((33332+1)*8))
OCR3A = 33332;
// CTC
TCCR3B |= (1 << WGM32);
// Prescaler 8
TCCR3B |= (1 << CS31);
// Output Compare Match A Interrupt Enable
TIMSK3 |= (1 << OCIE3A);
*/
// 240.00960038401536 Hz (16000000/((8332+1)*8))
OCR3A = 8332;
// CTC
TCCR3B |= (1 << WGM32);
// Prescaler 8
TCCR3B |= (1 << CS31);
// Output Compare Match A Interrupt Enable
TIMSK3 |= (1 << OCIE3A);
interrupts();
}
//using this to count 1hz, in case any buggy reports from telnet
//sometimes i see a lot of monitors light up, then immediately turn off
//on telnet, so accounting for that.
void setupTimer4() {
noInterrupts();
// Clear registers
TCCR4A = 0;
TCCR4B = 0;
TCNT4 = 0;
// 1 Hz (16000000/((15624+1)*1024))
OCR4A = 15624;
// CTC
TCCR4B |= (1 << WGM42);
// Prescaler 1024
TCCR4B |= (1 << CS42) | (1 << CS40);
// Output Compare Match A Interrupt Enable
TIMSK4 |= (1 << OCIE4A);
interrupts();
}
void WebServer (void){
/********************SERVER STATUS PAGE*********************/
/*
* With this, you can logon to the Sensor from your LAN to find
* out just what device this IP address is, in case you happen to
* forget.
*/
//Serial.println("In server");
//Server Status Page
// listen for incoming clients
EthernetClient client3 = server2.available();
if (client3) {
Serial.println(F("web visitor"));
// an http request ends with a blank line
boolean currentLineIsBlank = true;
while (client3.connected()) {
if (client3.available()) {
char c = client3.read();
Serial.write(c);
// if you've gotten to the end of the line (received a newline
// character) and the line is blank, the http request has ended,
// so you can send a reply
if (c == '\n' && currentLineIsBlank) {
// send a standard http response header
client3.println("HTTP/1.1 200 OK");
client3.println("Content-Type: text/html");
client3.println();
client3.println("<!DOCTYPE HTML>");
client3.println("<html><pre>");
client3.println("<b>Steak Electronics</b>");
client3.println("\"Steak it One Steak at a time.\"");
client3.println("");
//client.println("<b>IP Address:</b>");
//client.println(Ethernet.localIP());
//client.print("Sensor Location:");
//client.println(LOCATIONOFSENSOR);
client3.print("Type:");
client3.println("ZMHW Map");
client3.print("Last Activity was Monitor: ");
client3.println(MonFIN);
client3.println("</pre></html>");
break;
}
if (c == '\n') {
// you're starting a new line
currentLineIsBlank = true;
} else if (c != '\r') {
// you've gotten a character on the current line
currentLineIsBlank = false;
}
}
}
// give the web browser time to receive the data
delay(1);
// close the connection:
client.stop();
Serial.println(F("web visitor disconnected"));
}
}
void PixZero (void){
PORTE = 0b00010000;
//_delay_us(0.25);
PORTE = 0x00;
//_delay_us(0.25);
}
void PixOne (void){
PORTE = 0b00010000;
//_delay_us(0.55);
_delay_us(0.55);
PORTE = 0x00;
}
void PixBit (bool res){
if (res == false){
PixZero();
}
if (res == true){
PixOne();
}
}
void PixByte (char input){
uint8_t changer = 0;
//WS2812 reads bits as left is lowest (high in first), so go backwards
for(changer=8;changer>0;changer--){
PixBit(bitRead(input, changer));
//input <<= 1; //Atmega didn't like this, so instead, using changer
//instead of shifting input
}
}
void sendPixel(uint8_t g, uint8_t r, uint8_t b){
/*ws2812, reads bits left side as lowest*/
/*PixByte(0b10100000); //This is dim green
PixByte(0b00000000);
PixByte(0b00000000);
PixByte(0b00000000); //no white on my LED*/
PixByte(g);
PixByte(r);
PixByte(b);
//PixByte(w);
PORTE = 0x00;
}
void PulseRGB_g (uint8_t max, uint8_t r, uint8_t b){
int rgbfunc = 0;
for (rgbfunc = 0; rgbfunc < max; rgbfunc++){
sendPixel(rgbfunc,r,b);
delay(22);
}
for (rgbfunc = max; rgbfunc > 0; rgbfunc--){
sendPixel(rgbfunc,r,b);
delay(22);
}
}
void PulseRGB_r (uint8_t max, uint8_t g, uint8_t b){
int rgbfunc = 0;
for (rgbfunc = 0; rgbfunc < max; rgbfunc++){
sendPixel(g,rgbfunc, b);
delay(22);
}
for (rgbfunc = max; rgbfunc > 0; rgbfunc--){
sendPixel(g,rgbfunc, b);
delay(22);
}
}
void PulseRGB_b (uint8_t max, uint8_t g, uint8_t r){
int rgbfunc = 0;
for (rgbfunc = 0; rgbfunc < max; rgbfunc++){
sendPixel(g,r,rgbfunc);
delay(22);
}
for (rgbfunc = max; rgbfunc > 0; rgbfunc--){
sendPixel(g,r,rgbfunc);
delay(22);
}
}