Image

Image

Search This Blog

Wednesday, March 17, 2021

ESP Scale with calibration and tare

 #include "HX711.h"

#include "Wire.h"

#include "Adafruit_SSD1306.h"

#include "ESP8266WiFi.h"

#include "EEPROM.h"

#include "ESP8266httpUpdate.h"


#define SCREEN_WIDTH 128 

#define SCREEN_HEIGHT 32 

#define OLED_RESET     -1 

#define SCREEN_ADDRESS 0x3C 


#define upd_version "1"

#define upd_reboot true

#define upd_server "10.11.12.13"

#define upd_file "/update/scaleupd.php"


const int LOADCELL_DOUT_PIN = D4;

const int LOADCELL_SCK_PIN = D3;

const int TarePin = D5;

const int OverPin = D6;

const int GoodPin = D7;

const int UnderPin = D8;


int CalEepromAdress = 101;

int TarEepromAdress = 111;


float known_mass = 500;

float readValue = 0;

float calValue = 6192.2;// overrided by EEPROM or/and boot calibration

float tareValue = 472; // overrided by EEPROM or/and manual tare


const char* ssid = "myWiFi";

const char* password = "53cr37p4ssw0rd"; 


HX711 scale;

WiFiClient client;

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); //oled display uses std wire lib - for nodemcu SCL is D1(4), SDA is D2(5) (see pins_arduino.h)


void setup() {

  Serial.begin(115200);

  Serial.println("\nStarting...");

  display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS);

  display.clearDisplay();

  display.setTextSize(2);             

  display.setTextColor(SSD1306_WHITE);

  display.setCursor(0, 0);

  display.println("Press to  Calibrate");

  display.display(); 


  pinMode(TarePin, INPUT_PULLUP);

  pinMode(OverPin, OUTPUT);

  pinMode(GoodPin, OUTPUT);

  pinMode(UnderPin, OUTPUT);

  

  scale.begin(LOADCELL_DOUT_PIN, LOADCELL_SCK_PIN);

  delay(200); 

  scale.set_scale();

  scale.set_scale(calValue); 

  scale.tare();

 

  int wificnt = 0;

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED && wificnt < 10) {

   Serial.print (".");

   wificnt ++;

   delay(200);

  } //try 20sec to connect to Wifi in order to perform OTA update

  ESPhttpUpdate.rebootOnUpdate(upd_reboot);

  t_httpUpdate_return ret = ESPhttpUpdate.update(upd_server, 80, upd_file, upd_version);


  EEPROM.begin(512);

  EEPROM.get(CalEepromAdress, calValue);

  EEPROM.get(TarEepromAdress, tareValue);

 delay(2000); //Giving two sec to perform calibration at boot

  if(digitalRead(TarePin) == LOW){

   Serial.println("\nIn Calibration...");

   scale.set_scale();

   scale.tare();

   digitalWrite(UnderPin, HIGH); //turning all leds ON

   digitalWrite(OverPin, HIGH);

   digitalWrite(GoodPin, HIGH);

   display.clearDisplay();

   display.setCursor(0,0); 

   display.print("Calibrate with "); display.println(known_mass);

   display.display();      

   Serial.print("Calibrate with "); Serial.println(known_mass);

   delay(2000); //Two more seconds to release the button

   while (digitalRead(TarePin) == HIGH){ delay(200); Serial.print("."); } //Wait for the button to be pushed, after putting the calibrated weight on the scale

    calValue = (scale.get_units(10)) / known_mass;

    EEPROM.put(CalEepromAdress, calValue); //Save the calibration to EEPROM

    display.clearDisplay();

    display.setCursor(0,0); 

    display.print("Calibrated to "); display.println(calValue);

    display.display();

    Serial.print("\nCalibrated to "); Serial.println(calValue);

    scale.set_scale(calValue);

    delay(1000);

  }

  

  tareValue = (scale.get_units(20)); //Perform an initial tare

  scale.set_offset(tareValue);

  scale.tare();EEPROM.put(TarEepromAdress, tareValue); //Save TARE into EEPROM - not really needed here

  EEPROM.end();

 

}


void loop() {

 display.clearDisplay();

 

 if(digitalRead(TarePin) == LOW){ //if button pressed - perform tare

  Serial.println("\nTare...");

  digitalWrite(UnderPin, HIGH); //turn all leds on

  digitalWrite(GoodPin, HIGH);

  digitalWrite(OverPin, HIGH);

  

  tareValue = (scale.get_units(20)); 

  scale.set_offset(tareValue);

  display.clearDisplay();

  display.setTextSize(2);            

  display.setCursor(0,0);  

  display.print(" TARE to  "); display.println(tareValue);

  display.display(); 

  Serial.print("TARE to "); Serial.println(tareValue);

 } //end of TARE


 readValue = (scale.get_units(10) - tareValue);

 //readValue = (scale.read_average(20) - tareValue);

 Serial.println(readValue);


 display.clearDisplay();

 display.setTextSize(4);

 display.setCursor(5, 0);

 display.print(readValue); 

 display.setTextSize(2);

 display.setCursor(116, 17);

 display.println("g");

 display.display(); 


 digitalWrite(UnderPin, LOW); //turn all leds off

 digitalWrite(GoodPin, LOW);

 digitalWrite(OverPin, LOW);


 if(readValue < 3.49){

  digitalWrite(UnderPin, HIGH); //Red Led - below 3.5g 

 }

 if(readValue > 3.48 && readValue < 3.80){

  digitalWrite(GoodPin, HIGH); //Green Led - OK

 }

 if(readValue > 3.79){

  digitalWrite(OverPin, HIGH); //Blue Led - too heavy

 }  

//  delay(100);

}

 


 

Wednesday, February 03, 2021

ESP Scale pushing to server

 include "HX711.h"

#include "ESP8266WiFi.h"

#include "EEPROM.h"

#include "ESP8266httpUpdate.h"


#define upd_version "6a"

#define upd_reboot true

#define upd_server "123.45.67.89"

#define upd_file "/update/index.php"


const int LOADCELL_DOUT_PIN = D4;

const int LOADCELL_SCK_PIN = D3;


int CalEepromAdress = 101;

int TarEepromAdress = 111;

int DevEepromAdress = 121;


float calibrationValue = 442.06;// See ScaleCalibrate.ino for this. override by EEPROM

float tareValue = 472; // override by EEPROM

String thisScale = "LC000"; // override by EEPROM


char server[] = "123.45.67.90";

String uploadfile = "process.php";

String postVariable = "data=";

const char* ssid = "MyWiFi";

const char* password = "MyP@ssword";


HX711 scale;

WiFiClient client;

ADC_MODE(ADC_VCC);


void setup() {

//  Serial.begin(115200);

 

  EEPROM.begin(512);

  EEPROM.get(CalEepromAdress, calibrationValue);

  EEPROM.get(TarEepromAdress, tareValue);

  EEPROM.get(DevEepromAdress, thisScale);

  EEPROM.end();

 

  scale.begin(LOADCELL_DOUT_PIN, LOADCELL_SCK_PIN);

  scale.set_scale(calibrationValue); 

  

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {

   delay(200);

  }


  ESPhttpUpdate.rebootOnUpdate(upd_reboot);

  t_httpUpdate_return ret = ESPhttpUpdate.update(upd_server, 80, upd_file, upd_version);


}


void loop() {

  String postData = postVariable + thisScale + "," + ESP.getVcc() + "," + (scale.get_units(10) - tareValue) + "," + upd_version;


  if (client.connect(server, 80)) {

    client.print("POST /");

    client.print(uploadfile);

    client.println(" HTTP/1.1");

    client.print("Host: ");

    client.println (server);

    client.println("Content-Type: application/x-www-form-urlencoded");

    client.print("Content-Length: ");

    client.println(postData.length());

    client.println();

    client.print(postData);

 }

  if (client.connected()) {

      client.stop();

  }

  scale.power_down();

  ESP.deepSleep(300e6);

}

Saturday, January 02, 2021

ESP OTA update - server side

 <?php

date_default_timezone_set('America/New_York');

header('Content-type: text/plain; charset=utf8', true);

 

$pattern = ".php";

$debuglog = "/var/log/ESP8266-ESPUpdate.log";

 

//update files must be named "LoadCell-$Version_number.bin"

foreach(glob('./bin/LoadCell*.{bin}', GLOB_BRACE) as $filename){

     $filenm = pathinfo($filename, PATHINFO_FILENAME);

     $arr = explode('-', $filenm);

     $upd_version  = $arr[ count($arr) -1 ];

     error_log("\n".'['.date('Y-M-d H:m:s').'] Filename: '.$filename.' Shortname: '.$filenm.' Version: '.$upd_version."\n", 3, $debuglog);

}

 

function check_header($name, $value = false) {

    if(!isset($_SERVER[$name])) {

        return false;

    }

    if($value && $_SERVER[$name] != $value) {

        return false;

    }

    return true;

}

 

function sendFile($path) {

    header($_SERVER["SERVER_PROTOCOL"].' 200 OK', true, 200);

    header('Content-Type: application/octet-stream', true);

    header('Content-Disposition: attachment; filename='.basename($path));

    header('Content-Length: '.filesize($path), true);

    header('x-MD5: '.md5_file($path), true);

    readfile($path);

}

 

if(!check_header('HTTP_USER_AGENT', 'ESP8266-http-Update'))

{

    header($_SERVER["SERVER_PROTOCOL"].' 403 Forbidden', true, 403);

    echo "only for ESP8266 updater! (UA)\n";

    error_log("\n".'['.date('YYYY-MM-dd HH:mm:ss').'] Not correct user_agent.\n\n', 3, $debuglog);

    exit();

}

 

if(

    !check_header('HTTP_X_ESP8266_STA_MAC') ||

    !check_header('HTTP_X_ESP8266_AP_MAC') ||

    !check_header('HTTP_X_ESP8266_FREE_SPACE') ||

    !check_header('HTTP_X_ESP8266_SKETCH_SIZE') ||

    !check_header('HTTP_X_ESP8266_SKETCH_MD5') ||

    !check_header('HTTP_X_ESP8266_CHIP_SIZE') ||

    !check_header('HTTP_X_ESP8266_SDK_VERSION')

) {

    header($_SERVER["SERVER_PROTOCOL"].' 403 Forbidden', true, 403);

    echo "only for ESP8266 updater! (header)\n";

    error_log("\n".'['.date('Y-M-d H:m:s').'] Not correct headers.\n\n', 3, $debuglog);

    exit();

}

 

//    foreach (getallheaders() as $name => $value) {

//    error_log("\n".'['.date('Y-M-d H:m:s').'] Header_name: '.$name.": \t".$value, 3, $debuglog);

//    }

 

if (($_SERVER['HTTP_X_ESP8266_VERSION']) < $upd_version && $_SERVER["HTTP_X_ESP8266_SKETCH_MD5"] != md5_file($filename)) {

    sendFile($filename);

    error_log("\n".'['.date('Y-M-d H:m:s').'] NEED UPDATE! ', 3, $debuglog);

    error_log("\n".'['.date('Y-M-d H:m:s').'] Ver on server: '.($upd_version).' Ver remote: '.($_SERVER['HTTP_X_ESP8266_VERSION']), 3, $debuglog);

    error_log("\n".'['.date('Y-M-d H:m:s').'] MD5 on server: '.(md5_file($filename)).' Value remote: '.($_SERVER['HTTP_X_ESP8266_SKETCH_MD5'])."\n", 3, $debuglog);

} else {

    error_log("\n".'['.date('Y-M-d H:m:s').'] NO NEED. Ver equal, small or same MD5', 3, $debuglog);

    error_log("\n".'['.date('Y-M-d H:m:s').'] Ver on server: '.($upd_version).' Ver remote: '.($_SERVER['HTTP_X_ESP8266_VERSION']), 3, $debuglog);

    error_log("\n".'['.date('Y-M-d H:m:s').'] MD5 on server: '.(md5_file($filename)).' Value remote: '.($_SERVER['HTTP_X_ESP8266_SKETCH_MD5'])."\n", 3, $debuglog);

}

 

?>

Wednesday, December 02, 2020

ESP WiFi sniffer with OLED and http upload

#include "ESP8266WiFi.h"
#include "oled.h"
ADC_MODE(ADC_VCC);
OLED Display=OLED(D1,D2);
int RawPort = A0;
char server[] = "192.168.1.101";
String uploadfile = "wifi.php";
String postVariable = "WiFi=";
const char* ssid = "MyWiFi";
const char* password = "P@55w0rd";
String total = "";
void setup() {
//  Serial.begin(115200);
//  Serial.println("");
  Display.begin();
  Display.setTTYMode(true);
  pinMode(LED_BUILTIN, OUTPUT);
  WiFi.mode(WIFI_STA);
  WiFi.disconnect();
  delay(100);
  Display.print("Battery: ");
  Display.print(ESP.getVcc());
//  Display.print("\n\rSetup done");
}
void loop() {
//  int Batt = ESP.getVcc();
//  Serial.print("Battery voltage is: "+Batt);
//  Display.print(ESP.getVcc());
//  Serial.print("Wifi scan...");
  Display.set_contrast(128);
  Display.print("\n\rScan...");
  int n = WiFi.scanNetworks();
//  Serial.println(" done");
  Display.println(" done");
  if (n == 0) {
//    Serial.println("No Networks Found");
    Display.printf("No Networks Found");
    digitalWrite(LED_BUILTIN, LOW);
  }
  else {
//    Serial.print(n);
//    Serial.println(" Networks found");
    digitalWrite(LED_BUILTIN, HIGH);
    Display.set_contrast(8);
    Display.printf("%d networks found",n);
    String nr = String(n) + " APs detected. ";
    for (int i = 0; i < n; ++i) {
//      Serial.print(i + 1);
//      Serial.print(": ");
//      Serial.print(WiFi.SSID(i));
//      Serial.print(" RSSI: ");
//      Serial.print(WiFi.RSSI(i)); 
//      Serial.print(" MAC: ");
//      Serial.print(WiFi.BSSIDstr(i));
//      Serial.print(" Enc: ");
//      Serial.println(encType(i));
    Display.println();
    Display.print(i + 1);
    Display.print(": ");
    Display.print(WiFi.SSID(i));
    Display.print(" (");
    Display.print(WiFi.RSSI(i));
    Display.println(")");
    Display.print("   ");
    Display.print(WiFi.BSSIDstr(i));
    total = total+String(i+1)+": "+String(WiFi.SSID(i))+" "+String(WiFi.BSSIDstr(i))+" "+String(WiFi.RSSI(i))+"db "+String(encType(i))+". ";
    delay(500);
    }
 
    WiFi.scanDelete();
    delay (500);
    Display.println("\n\rSleep for 1min...");
    
    WiFiClient client;
    WiFi.begin(ssid, password);
    while (WiFi.status() != WL_CONNECTED) {
     delay(500);
    }
    
    
    String postData = postVariable + nr + total;
//    Serial.print("Transmitted data: ");
//    Serial.println(postData);
    
    if (client.connect(server, 80)) {
     client.print("POST /");
     client.print(uploadfile);
     client.println(" HTTP/1.1");
     client.print("Host: ");
     client.println (server);
     client.println("Content-Type: application/x-www-form-urlencoded");
     client.print("Content-Length: ");
     client.println(postData.length());
     client.println();
     client.print(postData);
    }
    if (client.connected()) {
      client.stop();
    }
//   Serial.println("Sleeping ");
   Display.set_power(false);
//   delay(10000);
   ESP.deepSleep(6e+7); // 60 sec  
  }
}
// Readable Encryption Type
String encType(int id){
String type;
  if(WiFi.encryptionType(id) == ENC_TYPE_WEP){ type=" WEP";
  }else if(WiFi.encryptionType(id) == ENC_TYPE_TKIP){ type="WPA-TKIP";    
  }else if(WiFi.encryptionType(id) == ENC_TYPE_CCMP){ type="WPA2-CCMP";    
  }else if(WiFi.encryptionType(id) == ENC_TYPE_AUTO){ type="WPA-WPA2";    
  }else if(WiFi.encryptionType(id) == ENC_TYPE_NONE){ type="OPEN";    
  }
  return type;
}




$cat wifi.php
<?php
$file = fopen("/var/www/html/wifi.txt", "a+") or die("Unable to open file!");
date_default_timezone_set("America/New_York");
$time = date('Y-m-d_H:i:s');
$read = $_POST["WiFi"];
$data = "{$time} - {$read}\n";
fwrite($file, $data);
fclose($file);
?>




Monday, November 02, 2020

Check if WiFi is still connected on rpi

 #!/bin/bash


gateway='192.168.0.1'

lockfile='/var/run/chk-wifi.pid'

wlan='wlan0'



if [ -e $lockfile ]; then

    echo `date +%F\ %T\ `$0": A lockfile exists... Lets check to see if it is still valid"

    pid=`cat $lockfile`

    if kill -0 &>1 > /dev/null $pid; then

     echo `date +%F\ %T\ `$0": Process still running, Lockfile valid."

     exit 1

    else

     echo `date +%F\ %T\ `$0": Invalid Lockfile, Removing."

     rm $lockfile

    fi

fi


#echo `date +%F\ %T\ `$0": Setting Lockfile"

echo $$ > $lockfile


#echo `date +%F\ %T\ `$0": Performing network check for $wlan"

ping -c2 $gateway 2>&1

rc=$?

if [[ $rc -eq 0 ]] ; then

        echo `date +%F\ %T\ `$0": The network is up."

else

        echo `date +%F\ %T\ `$0": Network down! Attempting reconnection."

        ifdown $wlan

#        echo `date +%F\ %T\ `$0": Cycling rpi3 onboard wifi driver"

        rmmod brcmfmac

        sleep 2

#        echo `date +%F\ %T\ `$0": Most probable cause in rpi1-2 is USB jamming, cycling the power on USB"

#        echo 0 > /sys/devices/platform/soc/20980000.usb/buspower

#        sleep 2

#        echo 1 > /sys/devices/platform/soc/20980000.usb/buspower

#        sleep 1

#        echo `date +%F\ %T\ `$0": Bringing back the WiFi"

        modprobe brcmfmac

        ifup --force $wlan

#        sleep 1

#        echo `date +%F\ %T\ `$0": New IP: $(ifconfig $wlan | grep 'inet addr')"

#        echo `date +%F\ %T\ `$0": "$(hostname -I)

fi


#echo `date +%F\ %T\ `$0": Remove lock & exit"

rm $lockfile

exit 0


Thursday, October 22, 2020

Wednesday, April 08, 2020

DD-WRT on a Videotron reflashed DIR-825

The Videotron custom firmware does not allow local upgrade. On the firmware update page there is a nice "Firmware upgrade: Disabled" and that's where the update ends in the main interface.
In order to update, I've rebooted in  recovery mode browser by keeping the reset pressed white plugin the power. However, the upload process was always staying at 0% and didn't finished;
Then I've hexedited the factory-to-ddwrt_NA.bin and changed the last byte in the file from "0" to "2" - still no luck;
I've tried uploading directly from the shell: "ifconfig enp0s25 192.168.0.34 up && curl -0vF files=@factory-to-ddwrt_NA.bin http://192.168.0.1/cgi/index" - nope, still nothing;

The solution was deceivingly simple: I've put a switch between the D-Link and the computer - then the curl upload worked perfectly!

Thursday, February 13, 2020

Asterisk PAGE say time every hour

Digium D6x phones and CyberData SIP Speakers  are used to page.
The phones are also used as intercoms (bidirectional page).

To start, we need accounts for the phones/speakers added to sip.conf:



[phone1] ; Phone
type=friend
host=dynamic
context=my-context
secret=5678
mailbox=319

[speaker1]; Speaker
type=friend 
host=dynamic
context=my-context
secret=1234
mailbox=329
record_out=Adhoc
record_in=Adhoc
qualify=no


Then in extensions.conf, in the [my-context] context, add:

; Paging extensions
exten => 3319,1,GotoIf($[ ${CALLERID(number)} = 319 ]?skipself)
exten => 3319,1,SIPAddHeader(Alert-Info: info=<intercom>) ; Digium D6x require this to enable paging - search documentation for different phone models!
exten => 3319,n,Dial(SIP/phone1) ; this is the phone1 defined in sip.conf
exten => 3319,n(skipself),Noop(Not paging originator)

exten => 3329,1,GotoIf($[ ${CALLERID(number)} = 329 ]?skipself)
exten => 3329,n,Dial(SIP/speaker1,50) ; this is the speaker1 defined in sip.conf
exten => 3329,n(skipself),Noop(Not paging originator)

exten => 398,1,Page(LOCAL/3319@my-context&LOCAL/3329@my-context,di,120) ; Bidirectional PAGE - that's what the "d" does.
exten => 398,n,Hangup()

With this, we can dial 398 and the PAGE should work.

Now, in order to say the time automatically, we need a .call file, let's create /var/lib/asterisk/third-party/say-time.call

Channel: LOCAL/398@my-context
MaxRetries: 10
RetryTime: 5
WaitTime: 20
Context: page-say-time
Extension: 3310

Of course, we need to create the [page-say-time] context in extensions.conf:

[page-say-time]
exten => 3310,1,Answer()
exten => 3310,n,Wait(1)
exten => 3310,n,Playback(at-tone-time-exactly) ; this sound file is already in asterisk sounds
exten => 3310,n,Wait(1)
exten => 3310,n,SayUnixTime(,EST,IMp)
exten => 3310,n,Wait(1)
exten => 3310,n,Playback(beep) ; this sound file is already in asterisk sounds
exten => 3310,n,Wait(2)
exten => 3310,n,Hangup()

and the last step, create a crontab that copies the say-time.call to the astersk outgoing at every fix hour:

0  * *  *  * /bin/cp /var/lib/asterisk/third-party/say-time.call /var/spool/asterisk/outgoing/


Tuesday, January 28, 2020

Copy standard switch port groups from one esx host to another

The below code asks for lmvap-vcs60 credentials, connects to Vcenter, and copied aesx11 vSwitch2 portgroups over to a new host called aesx05 using vSwitch1. (or so said my buddy Josh O. who wrote it) 

$vccred = get-credential
connect-viserver -server lmvap-vcs60.domain.tld -credential $vccred
$dest = get-virtualswitch -name vSwitch1 -vmhost aesx05.domain.tld
$source = get-virtualportgroup -vmhost aesx11.domain.tld -virtualswitch vSwitch2 -standard
$countvar = $source.count
for ($a=0 ; $a -le $countvar-1 ; $a++)
{
$pgname = $source[$a].name
$vlan = $source[$a].VLANID
new-virtualportgroup -virtualswitch $dest -name $pgname -VLANID $vlan
}
disconnect-viserver -server lmvap-vcs60.domain.tld -confirm:$false



Wednesday, October 16, 2019

14

and going strong!

Wednesday, July 03, 2019

DD-WRT wireless extender

Setup -> Basic Setup -> WAN Connection Type -> Connection Type ->Disabled

Network Setup -> Router IP -> Local IP Address - choose an IP outside the DHCP Range from the main router
Gateway and Local DNS - usually the IP of the main router

Wireless -> Basic Settings -> First interface (2.4GHz) -> Wireless Mode: Client Bridge (Routed)
Default GW Mode: Manual
Gateway: IP of the main router
Wireless Security: same as on main router

Wireless -> Basic Settings -> Second interface (5GHz) -> Wireless Mode: AP
Set the WiFi network the way you want - You can duplicate the 5GHz config form the main router. this way the clients will do seamless roaming.

Services -> disable all
Security -> disable all
Access Restrictions -> disable all
NAT / QoS -> disable all

Administration -> Management
802.1x: Disable
Reset Button: Disable
Routing: Disable

Administration -> Keep Alive
Enable Watchdog: Enable
Interval (in seconds): 900
IP Addresses: Ip of the main router

Tuesday, June 04, 2019

Web Interface for Parental Control

This continues the Parental Control post from last month.

First of all, in order to protect the web page, we need an authentication method. A simple user/password will do for the moment (it's not perfect, you can bypass it by accessing directly the /cgi-bin/script.sh, but for the purpose of this exercise is OK-ish) .

Make sure that in the lighttpd.conf, mod_auth and mod_access are loaded,
server.modules += ( "mod_access" )
server.modules += ( "mod_auth" )

and the host section is protected

HTTP["url"] =~ "^/" {
auth.backend = "plain"
auth.backend.plain.userfile = "/jffs/lighttpd/.lighttpdpassword"
auth.require = ( "/" => (
"method" => "basic",
"realm" => "Password protected Parental Control",
"require" => "valid-user"
))}
(where /jffs/lighttpd/.lighttpdpassword contains the plaintext credentials, let's say parent:password)


The following index.html must be placed into the lighthttpd www root (/jffs/www/):

<html xmlns="http://www.w3.org/1999/xhtml">
 <head>
   <title>Parental Control</title>
     <form action="../cgi-bin/ai.sh" method="POST">
     <button name="name" value="value" style="background-color:lime;height:150px;width:400px"> Allow internet </button>
     </form><p><br>
     <form action="../cgi-bin/ag.sh" method="POST">
     <button name="name" value="value" style="background-color:yellowgreen;height:150px;width:400px">  Allow games  </button>
     </form><p><br>
     <form action="../cgi-bin/ay.sh" method="POST">
     <button name="name" value="value" style="background-color:khaki;height:150px;width:400px">  Allow only YouTube  </button>
     </form><p><br>
     <form action="../cgi-bin/ni.sh" method="POST">
     <button name="name" value="value" style="background-color:red;height:150px;width:400px"> No internet </button>
     </form><p><br>
     <form action="../cgi-bin/ng.sh" method="POST">
     <button name="name" value="value" style="background-color:lightcoral;height:150px;width:400px">  No games  </button>
     </form><p><br>
     <form action="../cgi-bin/lst.sh" method="POST">
     <button name="name" value="value" style="background-color:cyan;height:150px;width:400px">  Show actual  </button>
     </form>
 </head>
</html>

The following scripts will be placed into the ./cgi-bin folder:

ag.sh
#!/bin/sh
OUTPUT=$('/jffs/allow_game ; sleep 1; iptables -L FORWARD | grep DROP | grep -v "DROP       0    --  anywhere             anywhere" | if grep -q "DROP       0    --  192.168.1.128/28    anywhere"; then echo NO Internet; else echo Allow Internet; fi; if grep -qm1 "#" /tmp/yt-block.conf; then echo Allow YT; else echo NO YT; fi; if grep -qm1 "#" /tmp/games-block.conf; then echo Allow Games; else echo NO Games; fi' | awk 'BEGIN{print "<table>"} {print "<tr>";for(i=1;i<=NF;i++)print "<td>" $i"</td>";print "</tr>"} END{print "</table>"}')
echo "Content-type: text/html"
echo ""
echo "<html><head><title>Parental Control</title></head><body>"
echo "Rules are: $OUTPUT <br><p>"
echo "<form><input type='button' style='background-color:cyan;height:200px;width:400px' value='Back' onclick='history.back()'></form>"
echo "</body></html>"

ai.sh
#!/bin/sh
OUTPUT=$('/jffs/del_fw ;sleep 1; iptables -L FORWARD | grep DROP | grep -v "DROP       0    --  anywhere             anywhere" | if grep -q "DROP       0    --  192.168.1.128/28    anywhere"; then echo NO Internet; else echo Allow Internet; fi; if grep -qm1 "#" /tmp/yt-block.conf; then echo Allow YT; else echo NO YT; fi; if grep -qm1 "#" /tmp/games-block.conf; then echo Allow Games; else echo NO Games; fi' | awk 'BEGIN{print "<table>"} {print "<tr>";for(i=1;i<=NF;i++)print "<td>" $i"</td>";print "</tr>"} END{print "</table>"}')
echo "Content-type: text/html"
echo ""
echo "<html><head><title>Parental Control</title></head><body>"
echo "Rules are: $OUTPUT <br><p>"
echo "<form><input type='button' style='background-color:cyan;height:200px;width:400px' value='Back' onclick='history.back()'></form>"
echo "</body></html>"

ay.sh
#!/bin/sh
OUTPUT=$('/jffs/allow_yt ; sleep 1; iptables -L FORWARD | grep DROP | grep -v "DROP       0    --  anywhere             anywhere" | if grep -q "DROP       0    --  192.168.1.128/28    anywhere"; then echo NO Internet; else echo Allow Internet; fi; if grep -qm1 "#" /tmp/yt-block.conf; then echo Allow YT; else echo NO YT; fi; if grep -qm1 "#" /tmp/games-block.conf; then echo Allow Games; else echo NO Games; fi' | awk 'BEGIN{print "<table>"} {print "<tr>";for(i=1;i<=NF;i++)print "<td>" $i"</td>";print "</tr>"} END{print "</table>"}')
echo "Content-type: text/html"
echo ""
echo "<html><head><title>Parental Control</title></head><body>"
echo "Rules are: $OUTPUT <br><p>"
echo "<form><input type='button' style='background-color:cyan;height:200px;width:400px' value='Back' onclick='history.back()'></form>"
echo "</body></html>"

lst.sh
#!/bin/sh
OUTPUT=$('iptables -L FORWARD | grep DROP | grep -v "DROP       0    --  anywhere             anywhere" | if grep -q "DROP       0    --  192.168.1.128/28    anywhere"; then echo NO Internet; else echo Allow Internet; fi; if grep -qm1 "#" /tmp/yt-block.conf; then echo Allow YT; else echo NO YT; fi; if grep -qm1 "#" /tmp/games-block.conf; then echo Allow Games; else echo NO Games; fi;' | awk 'BEGIN{print "<table>"} {print "<tr>";for(i=1;i<=NF;i++)print "<td>" $i"</td>";print "</tr>"} END{print "</table>"}')
echo "Content-type: text/html"
echo ""
echo "<html><head><title>Parental Control</title></head><body>"
echo "Rules are: $OUTPUT <br><p>"
echo "<form><input type='button' style='background-color:cyan;height:200px;width:400px' value='Back' onclick='history.back()'></form>"
echo "</body></html>"

ng.sh
#!/bin/sh
OUTPUT=$('/jffs/disable_game && iptables -L FORWARD | grep DROP | grep -v "DROP       0    --  anywhere             anywhere" | if grep -q "DROP       0    --  192.168.1.128/28    anywhere"; then echo NO Internet; else echo Allow Internet; fi; if grep -qm1 "#" /tmp/yt-block.conf; then echo Allow YT; else echo NO YT; fi; if grep -qm1 "#" /tmp/games-block.conf; then echo Allow Games; else echo NO Games; fi' | awk 'BEGIN{print "<table>"} {print "<tr>";for(i=1;i<=NF;i++)print "<td>" $i"</td>";print "</tr>"} END{print "</table>"}')
echo "Content-type: text/html"
echo ""
echo "<html><head><title>Parental Control</title></head><body>"
echo "Rules are: $OUTPUT <br><p>"
echo "<form><input type='button' style='background-color:cyan;height:200px;width:400px' value='Back' onclick='history.back()'></form>"
echo "</body></html>"

ni.sh
#!/bin/sh
OUTPUT=$('/jffs/add_fw && iptables -L FORWARD | grep DROP | grep -v "DROP       0    --  anywhere             anywhere" | if grep -q "DROP       0    --  192.168.1.128/28    anywhere"; then echo NO Internet; else echo Allow Internet; fi; if grep -qm1 "#" /tmp/yt-block.conf; then echo Allow YT; else echo NO YT; fi; if grep -qm1 "#" /tmp/games-block.conf; then echo Allow Games; else echo NO Games; fi' | awk 'BEGIN{print "<table>"} {print "<tr>";for(i=1;i<=NF;i++)print "<td>" $i"</td>";print "</tr>"} END{print "</table>"}')
echo "Content-type: text/html"
echo ""
echo "<html><head><title>Parental Control</title></head><body>"
echo "Rules are: $OUTPUT <br><p>"
echo "<form><input type='button' style='background-color:cyan;height:200px;width:400px' value='Back' onclick='history.back()'></form>"
echo "</body></html>"

Now a very simple web page will allow you to control the kids internet from any browser:

Friday, May 03, 2019

Parental control

Because you can't let the kids on YouTube 24/7 and some games are really addictive :)

The router must run OpenWRT or DD-WRT.

The kids devices must be assigned IP's from a certain range, let's say 192.168.1.128/28 by adding some lines similar to the following one into Additional Dnsmasq Options:
dhcp-host=set:red,AA:BB:CC:00:DD:22,kids-tv,192.168.1.130,43200m


A series of scripts must be put in /jffs/ and called by a cron job:

cat add_fw
#!/bin/sh
iptables -I FORWARD 1 -s 192.168.1.128/28 -j DROP
iptables -I FORWARD 2 -s 192.168.1.128/28 -m conntrack -j DROP --ctstate RELATED,ESTABLISHED

cat del_fw
#!/bin/sh
iptables -D FORWARD -s 192.168.1.128/28 -j DROP
iptables -D FORWARD -s 192.168.1.128/28 -m conntrack -j DROP --ctstate RELATED,ESTABLISHED

cat disable_game
#!/bin/sh
# DNS Rules
sed -e 's/^#//' -i /tmp/games-block.conf
sed -e 's/^#//' -i /tmp/yt-block.conf
restart_dns
# Force kids DNS to local
iptables -t nat -A PREROUTING -i br0 -s 192.168.1.128/28 -p udp --dport 53 -j DNAT --to 192.168.1.1
iptables -t nat -A PREROUTING -i br0 -s 192.168.1.128/28 -p tcp --dport 53 -j DNAT --to 192.168.1.1
# Block all ports over :500
iptables -I FORWARD 5 -p tcp --source 192.168.1.128/28 --dport 500:65535 -j DROP

cat allow_game
#!/bin/sh
# Remove DNS rules
sed 's/^\([^#]\)/#\1/g' -i /tmp/games-block.conf
sed 's/^\([^#]\)/#\1/g' -i /tmp/yt-block.conf
restart_dns
# Remove Force kids DNS to local
iptables -t nat -D PREROUTING -i br0 -s 192.168.1.128/28 -p udp --dport 53 -j DNAT --to 192.168.1.1
iptables -t nat -D PREROUTING -i br0 -s 192.168.1.128/28 -p tcp --dport 53 -j DNAT --to 192.168.1.1
# Unblock all ports over :500
iptables -D FORWARD -p tcp --source 192.168.1.128/28 --dport 500:65535 -j DROP


I do have an extra script that allow access to YouTube, without allowing games, this one is called only from a html page that I'll explain in a later post:

cat allow_yt
#!/bin/sh
sed 's/^\([^#]\)/#\1/g' -i /tmp/yt-block.conf
restart_dns


Those scripts are called by cron jobs that makes sure we don't have internet during the sleep hours and games & YouTube are permitted only during the weekend:
00 21 * * 0-4 root /jffs/add_fw
30 22 * * 5,6 root /jffs/add_fw
00 08 * * * root /jffs/del_fw
30 17 * * 5 root /jffs/allow_game
30 17 * * 0 root /jffs/disable_game


In order to block the DNS requests, the following Additional Dnsmasq Options needs to be added:
conf-file=/tmp/yt-block.conf
conf-file=/tmp/games-block.conf


The files /tmp/yt-block.conf and /tmp/games-block.conf are created by the startup script:
stopservice dnsmasq
echo "#address=/.roblox.com/192.168.1.1
#address=/.rbxcdn.com/192.168.1.1
#address=/.epicgames.com/192.168.1.1
#address=/.fortnitegame.com/192.168.1.1
#address=/.easyanticheat.com/192.168.1.1
#address=/.pixelgunserver.com/192.168.1.1
#address=/.applovin.com/192.168.1.1
#address=/.clashroyaleapp.com/192.168.1.1
#address=/.applifier.com/192.168.1.1
#address=/.chartboost.com/192.168.1.1
#address=/.fyber.com/192.168.1.1
#address=/.twitch.tv/192.168.1.1
#address=/.ttvnw.net/192.168.1.1
#address=/.leagueoflegends.com/192.168.1.1
#address=/.pvp.net/192.168.1.1
#address=/.riotgames.com/192.168.1.1
#address=/.garenanow.com/192.168.1.1
#address=/.ea.com/192.168.1.1
#address=/.respawn.com/192.168.1.1
#address=/.origin.com/192.168.1.1" > /tmp/games-block.conf
echo "#address=/.youtube.com/192.168.1.1
#address=/youtube.googleapis.com/192.168.1.1
#address=/youtubei.googleapis.com/192.168.1.1
#address=/.ytimg.com/192.168.1.1
#address=/ytimg.l.google.com/192.168.1.1
#address=/youtube.l.google.com/192.168.1.1
#address=/.googlevideo.com/192.168.1.1
#address=/.youtube-nocookie.com/192.168.1.1
#address=/.youtu.be/192.168.1.1" > /tmp/yt-block.conf
startservice dnsmasq


An "easy" way to run those scripts besides the scheduled cron jobs, is from the DD-WRT Administration -> Commands page:

Monday, April 01, 2019

VM Management

# powershell script for VM mass management. Requires a .csv file containing the list of VMs and a name for the snapshot/ If no snapshot name is given, "Snapshot_1" is used.

 #load powercli if needed
if (!(Get-Module -Name VMware.VimAutomation.Core) -and (Get-Module -ListAvailable -Name VMware.VimAutomation.Core)) {
    Write-Output "loading the VMware Core Module..."
    if (!(Import-Module -Name VMware.VimAutomation.Core -ErrorAction SilentlyContinue)) {
        # Error out if loading fails
        Write-Error "`nERROR: Cannot load the VMware Module. Is the PowerCLI installed?"
     }
    $Loaded = $True
    }
 #   elseif (!(Get-Module -Name VMware.VimAutomation.Core -ErrorAction SilentlyContinue) -and !(Get-Module -Name VMware.VimAutomation.Core) -and ($Loaded -ne $True)) {
 #       Write-Output "loading the VMware Core Snapin..."
 #    if (!(Add-PSSnapin -PassThru VMware.VimAutomation.Core -ErrorAction SilentlyContinue)) {
 #    # Error out if loading fails
 #    Write-Error "`nERROR: Cannot load the VMware Snapin or Module. Is the PowerCLI installed?"
 #    }
 #   }

# Define vmConfigSpec params
$vmConfigSpec = New-Object VMware.Vim.VirtualMachineConfigSpec
$vmConfigSpec.Tools = New-Object VMware.Vim.ToolsConfigInfo
$vmConfigSpec.Tools.ToolsUpgradePolicy = "UpgradeAtPowerCycle"

# Get the command-line params
 $command = $args[0]
 $list = $args[1]
 $server = $args[2]

# define default params
if (!$list) { $list = "vm_mgmt.csv"}
if (!$server) { $server = "default.vCenter.domain.tld"}

# Start processing the command
  switch ($command) {
    default { $myname = $MyInvocation.MyCommand.Definition
    echo "`nERROR! Usage:"
    echo "$myname command [list] [server] "
    echo "`ncommand is one of the following: viewsnap, takesnap, delsnap, revertsnap, hwupd, vmtoolsupd, vmoff, vmon."
    echo " list is a .csv file containing 'VM_Name,Snapshot_name'. If no list provided, 'vm_mgmt.csv' will be used."
    echo " server is the name of the server connecting to. If no server is provided, 'default.vCenter.domain.tld' will be used.`n"
    }
 
  "viewsnap" {
    ### View Snapshot
      Connect-VIServer -Server $server -Protocol https
      import-csv $list | ForEach-Object {
      $_.VM_Name
      $_.Snapshot_name
      if (!$_.Snapshot_name) { $_.Snapshot_name = "Snaphot_1"}
    echo "`nView Snaphots:`n"
      get-vm -Name $_.VM_Name | get-snapshot
  } }

  "takesnap" {   
    ### Take Snapshot
      Connect-VIServer -Server $server -Protocol https
      import-csv $list | ForEach-Object {
      $_.VM_Name
      $_.Snapshot_name
      if (!$_.Snapshot_name) { $_.Snapshot_name = "Snaphot_1"}
    echo "`nTaking Snapshots`n"
      get-vm -Name $_.VM_Name | New-Snapshot -Name $_.Snapshot_1 -Quiesce -Memory
  } }

  "delsnap" {   
    ### Delete Snapshot
    Connect-VIServer -Server $server -Protocol https
    import-csv $list | ForEach-Object {
    $_.VM_Name
    $_.Snapshot_name
      if (!$_.Snapshot_name) { $_.Snapshot_name = "Snaphot_1"}
   echo "`nDelete Snapshots`n"
    get-snapshot -name $_.Snapshot_1 -vm $_.VM_Name | remove-snapshot -confirm:$false
  } }

  "revertsnap" {
    ### Revert To Snapshot
      Connect-VIServer -Server $server -Protocol https
      import-csv $list | ForEach-Object {
      $_.VM_Name
      $_.Snapshot_name
    echo "`nReverting Snapshots. Confirmation is required for each restore.`n"
      set-vm -VM $_.VM_Name -Snapshot $_.Snapshot_1 -whatif
    # set-vm -VM $_.VM_Name -Snapshot $_.Snapshot_1 -confirm:$false
  } }

  "hwupd" { 
    # VM Hardware upgrade
      Connect-VIServer -Server $server -Protocol https
      import-csv $list | ForEach-Object {
      $_.VM_Name
      $_.Snapshot_name
      if (!$_.Snapshot_name) { $_.Snapshot_name = "Snaphot_1"}
    echo "`nVM Hardware upgrade to vmx-13`n"
      $do = New-Object -TypeName VMware.Vim.VirtualMachineConfigSpec
      $do.ScheduledHardwareUpgradeInfo = New-Object -TypeName VMware.Vim.ScheduledHardwareUpgradeInfo
      $do.ScheduledHardwareUpgradeInfo.UpgradePolicy = “always”
      $do.ScheduledHardwareUpgradeInfo.VersionKey = “vmx-13”
      $vm.ExtensionData.ReconfigVM_Task($do)
  } }

  "vmtoolsupd" {
    # VM Tools update
      Connect-VIServer -Server $server -Protocol https
      import-csv $list | ForEach-Object {
      $_.VM_Name
      $_.Snapshot_name
      if (!$_.Snapshot_name) { $_.Snapshot_name = "Snaphot_1"}
    echo "`nUpdating VM Tools`n"
      get-vm -Name $_.VM_Name | %{$_.Extensiondata.ReconfigVM($vmConfigSpec)}
  } }

  "vmoff" {
    # VM power off
      Connect-VIServer -Server $server -Protocol https
      import-csv $list | ForEach-Object {
      $_.VM_Name
      $_.Snapshot_name
      if (!$_.Snapshot_name) { $_.Snapshot_name = "Snaphot_1"}
    echo "`nTurning VMs OFF`n"
      $vm = Get-VM -Name $_.VM_Name | Shutdown-VMGuest -Confirm:$false
  } }

  "vmon" {
    # VM power on
      Connect-VIServer -Server $server -Protocol https
      import-csv $list | ForEach-Object {
      $_.VM_Name
      $_.Snapshot_name
      if (!$_.Snapshot_name) { $_.Snapshot_name = "Snaphot_1"}
    echo "`nTurning VMs ON`n"
      $vm = Get-VM -Name $_.VM_Name | Start-VM -Confirm:$false
  } }

}



-----------------------------------------------
type vm_mgmt.csv

VM_NAME,Snapshot_name
some-vm-name,Snapshot_342
another-vm-name,Snapshot_temp4

Saturday, February 02, 2019

Chromium - Cast: "No Devices Found"

Starting chromium from the console you get "Component extension with id pkedcjkdefgpdelpbcmbmeomcjbeemfm not in whitelist and is not being loaded as a result."

- pkedcjkdefgpdelpbcmbmeomcjbeemfm is the Chrome Media Router, without it, you can go to chrome://flags and Enable the #load-media-router-component-extension as much as you want, no chromecast device will ever be detected.

My solution: 

1: copy pkedcjkdefgpdelpbcmbmeomcjbeemfm folder from a google-chrome profile ( ~/.config/google-chrome/Default/Extensions/pkedcjkdefgpdelpbcmbmeomcjbeemfm) to the chromium profile ~/.config/chromium/Default/Extensions/pkedcjkdefgpdelpbcmbmeomcjbeemfm
( If you copy from a windows installation, make sure the files are chmod 600, the folders are 700 and you are the right owner)

2: in Chromium, go to chrome://extensions/, enable "Developer Mode", then "Load Unpacked" and point to ~/.config/chromium/Default/Extensions/pkedcjkdefgpdelpbcmbmeomcjbeemfm

3: Now Chrome Media Router is loaded and the chromecast devices will be detected:

Wednesday, January 16, 2019

drakboot "INTERNAL ERROR: unknown device"


1: obviously, run strace drakboot > /tmp/trace 2>&1

2: less /tmp/trace, look for the missing device (in my case it was sdd):
 openat(AT_FDCWD, "/boot/grub2/install.sh", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = 12
ioctl(12, TCGETS, 0xbfb7147c)           = -1 ENOTTY (Inappropriate ioctl for device)
_llseek(12, 0, [0], SEEK_CUR)           = 0
fstat64(12, {st_mode=S_IFREG|0755, st_size=22, ...}) = 0
read(12, "grub2-install /dev/sdd", 8192) = 22
read(12, "", 8192)                      = 0

3: edit /boot/grub2/install.sh and replace the wrong disk reference.

Tuesday, January 08, 2019

Push-button ON/OFF

In order to start the Peltier-based car freezer and to make sure that it turn off and stays OFF after the car stopped (a big Peltier is a huge battery drain!), I've made this 4-relay based circuit. No or "as little as possible" electronics in the car is the best approach - only the aerospace is harder for electronics than automotive - the best solution might involve some relays and, maybe, a couple of diodes, but stay away from amateurish electronics in the car! This is the diagram I draw on the back of a napkin (as usual) :)


Monday, October 15, 2018

13

All good!

Thursday, May 10, 2018

RDP Disconnected! Error Code: 2308 Error Description: Socket closed

After a windows update, a couple of Windows 2016 Servers on AWS started rejecting the RDP connections.
mRemote was giving the error "RDP Disconnected! Error Code: 2308 Error Description: Socket closed". MS RDP is giving "This computer can't connect to the remote computer. Try connecting again. If the problem continues, contact the owner of the remote computer or your network administrator."

After a bit of tinkering, I found that the problem seems to be the RDP TLS and encryption level.

To solve it:

- remote connect Registry Editor to the affected server and change the DWORD 
HKLM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp\SecurityLayer from "2" to "0" 


- remote connect Services.msc to the affected server and restart TermService

Saturday, December 02, 2017

Windows could not complete the installation. To install windows on this computer restart the installation

SHIFT-F10 to bring up command prompt.
cd c:\windows\system32\oobe
msoobe.exe
Make a generic account and password. 
Hit finish (if it requests a product key and you have one, enter it now.  if OEM/No key required, just finish). 
Set time/date. 
Finish.

Thursday, November 02, 2017

Extend LVM with a new disk

partprobe 
cat /proc/partitions - note the un-partitioned drive (ex: there is a /dev/xvdX but not /dev/xvdX1) 
cfdisk /dev/xvdX - create new partition, type 8e (LVM) 
pvcreate /dev/xvdX1 
vgdisplay  - note the VG name (in my case is "vg_bkp_vol_1")
vgextend vg_bkp_vol_1 /dev/xvdX1 
pvscan 
lvdisplay  - note the LV path (here "/dev/vg_bkp_vol_1/lv_bkp_vol_1")
lvextend /dev/vg_bkp_vol_1/lv_bkp_vol_1 /dev/xvdX1 
lvdisplay 
vgdisplay 
resize2fs /dev/vg_bkp_vol_1/lv_bkp_vol_1 
df 

Blog Archive