2020/06/24

Primer contacto con ESP8266

En el marco de la cursada de CEIoT, en mi trabajo práctico que cuando termine compartiré, debo implementar unos sensores y actuadores con un microcontrolador. Ya he hecho algo parecido con una EDU-CIAA-NXP en germinómetro con SAPI y ando en regular la velocidad de unos coolers con un ATMega328p con ArduinoIDE  uno de estos días.

Esta vez debo agregarle conexión por WiFi y MQTT y aunque la EDU-CIAA-NXP con un módulo WiFi ESP-01 me serviría, prefiero algo más compacto. Además mi idea es instalar muchos más y la EDU-CIAA-NXP es un poco carita y me sirve para otras cosas.

El ATMega328p está regalado en términos de precio pero tambien tengo que agregarle un modulo WiFi ESP-01 y armar la placa, quizás más adelante.

Decidí usar una ESP-12x con FreeRTOS, relato acá mis desventuras para regocijo y utilidad tuya.

Lo primero es comprender qué es, por que miré bastante por ahí y hallé que hay un plugin para ArduinoIDE, que hay algo de Lua, que hay un firmware.

Si vas al sitio que dice el chip http://en.doit.am/, es todo un ecosistema y por más que sean los Doctors of Intelligence & Technology, no lo son de UX.

Hay dos links interesantes, LuaNode y en el desplegable NodeMCU, NodeMCU Start, vamos por el segundo, que nos lleva a https://smartarduino.gitbooks.io/user-manual-for-esp-12e-devkit/content/ que dice:

ESP-12E DevKit is already built-in Lua fireware with AP mode y que te tiene que aparecer un Access Point con una clave 12345678, triste, y una IP donde se supone que hay un servidor que te permite prender y apagar los leds.

Mmmh, ¿será esto lo que viene precargado como firmware? Parecido, la red se llama ESP_3d3d65, no tiene clave, me asigna la ip 192.168.4.3 y nadie escucha en 192.168.4.1.

Tengo otro microcontrolador de otra compra, los resultados son similares, tenemos un Access Point sin alma.

De todos modos no me interesa, como diría la Zorra al no alcanzar las Uvas.

Si vamos por el link de LuaNode, llegamos a https://github.com/Nicholas3388/LuaNode, que sin ser ninguna Zorra esta vez, me puede llegar a interesar otro día, hoy no por que no es mi camino de FreeRTOS.

Por el lado de ArduinoIDE, https://naylampmechatronics.com/blog/56_usando-esp8266-con-el-ide-de-arduino.html nos muestra como hacer el blinky pero antes aclara un poco el ecosistema esp8266, no lo voy a repetir acá, tambien está en https://en.wikipedia.org/wiki/ESP8266, que es donde debí haber comenzado, pero no sé por qué se me ocurrió tomar este acercamiento tangencial.

Finalmente, http://esp8266.net/, de ahí a:

git clone https://github.com/espressif/ESP8266_RTOS_SDK.git

El README.md dice de bajar la toolchain, luego vas al hello world, hacés make menuconfig, que te dice que te faltan un montón de cosas

~/ESP8266_RTOS_SDK/examples/get-started/hello_world$ make
...
setuptools
click>=5.0
pyserial>=3.0
future>=0.15.2
cryptography>=2.1.4
pyparsing>=2.0.3,<2.4.0
pyelftools>=0.22
...

y sugiere y acepto ejecutar:

/usr/bin/python -m pip install --user \
-r /home/ceiot/Desktop/ESP8266_RTOS_SDK/requirements.txt

pero antes

sudo apt install python-pip python-setuptools

Ahora make dice que falta curses.h, no problemo,

sudo apt install libncurses5-dev

falta gperf...

sudo apt install gperf

Y por fin..


esp_rtos_sdk


¡Esto se va a descontrolar! Hacía como diez o quince años que no veía uno de estos diálogos así, cuando tenía que compilar el kernel. En realidad que no interactuaba, estoy seguro que para algo de algún embebido vi algo el año pasado.


Y ahora make, que falla por que no encontró xtensa-lx106-elf-gcc, completamente razonable pues a esos paths que se mencionan no tienen nada que ver, les debe haber quedado la documentación sin actualizar, no importa, buscás donde descomprimiste:

export PATH=/home/ceiot/Desktop/xtensa-lx106-elf/bin:$PATH

Si te encontrás con estos errores, metiste la pata igual que yo, frená un momento y pensá...


ESP8266_RTOS_SDK/components/esp8266/driver/ir_rx.c: In function 'ir_rx_intr_handler': 
ESP8266_RTOS_SDK/components/esp8266/driver/ir_rx.c:59:5: error: missing braces around
 initializer [-Werror=missing-braces]
      static ir_rx_nec_data_t ir_data = {0};

que está reportado en https://github.com/espressif/ESP8266_RTOS_SDK/issues/761. Se "arregla" con:

static ir_rx_nec_data_t ir_data = {{0}}; 


Y ahí te das cuenta que te faltó un:

git checkout release/v3.3

para lo cual tenés que reclonear por que choca con los git pull sobre los components.

Y sigue sin funcionar, ahora dice:


ESP8266_RTOS_SDK/components/esp8266/source/esp_err_to_name.c:391:29: error: unknown type name 'esp_err_t'
 const char *esp_err_to_name(esp_err_t code)

Y finalmente te das cuenta que leiste el README.md de master, no el de release/v3.3 y te bajaste la toolchain equivocada, lo arreglás y ni te molestás en probarlo, te vás directo a:

~ESP8266_RTOS_SDK/examples/protocols/esp-mqtt/tcp$ make menuconfig

En Example configuration, que en la imagen anterior no está, ponés los datos de tu acceso a la WiFi:

Example Configuration  --->  
() WiFi SSID
() WiFi Password
(mqtt://iot.eclipseorg) Broker URL

Luego compilás, flasheas y te colgás del serial:


~ESP8266_RTOS_SDK/examples/protocols/esp-mqtt/tcp$ make all flash monitor
...
Connecting....
Chip is ESP8266EX
Features: WiFi
MAC: cc:50:e3:xx:xx:xx
...

Primer boot



ahí tenés la IP y la MAC y funciona todo ok.

Si no hubiera código mostrando la IP, como en el proceso de flash te dijo la MAC, la podés buscar así:

$arp -a |  grep cc:50
? (192.168.1.110) at cc:50:e3:xx:xx:xx [ether] on enp0s3

Si quisiera usar otro programa de terminal, por algún perverso motivo baudrate es 74880, lo pesqué con un git grep -i baud y probé los valores obtenidos.


Se cambia con make menuconfig  en la opción serial flasher config -> make monitor baud rate


Habiendo tomado el control del hello wold, me doy por satisfecho, cuando reporte el trabajo práctico completo mostraré más detalles, dame unos meses... quizás antes si surge algo interesante.





2020/06/14

Ayudas para gestionar archivos duplicados.

Una de mis tantas tareas postergadas que #quedateencasa me está permitiendo encarar, es limpiar un poco mi máquina de cosas repetidas, el tiempo pasa y a veces bajo algo otra vez pues no recuerdo haberlo hecho antes, tengo un proyecto al que le hice una copia por backup, tengo un proyecto que ha estado en distintas máquinas y cuando estoy por reformatear una copio a la principal y así. Mi disco tiene una selva parecida a esto:

Documentos
inbox
descargado
la datasheet

 anterior
Documentos
anterior
proyectos
precursor del proyecto

la datasheet

 inbox
clasificar
datashets
la datasheet

proyectos
precursor del proyecto

Desktop
repositorios
assembla
github
cpantel
el proyecto

doc
la datasheet

rescate
pendrive01
Desktop
el proyecto

la datasheet
 

Y lo mismo con instaladores, papers, libros, manuales, código mío y ajeno.

Me imagino que tendrás algo parecido.

Mas por salud mental que por el espacio ocupado, es mejorcito ordenar y limpiar.

Dado que en mi máquina por momentos pueden haber muchos, muchos archivos, no es una tarea sencilla y me he armado una serie de trucos/scripts para facilitarla.

Un millón y medio es un montón:

sudo find /home -type f | wc -l
790790

sudo find / -mount -type f | wc -l
718397

sudo es por que tu usuario no puede ver todo, de ahora en más hay uno implícito.

-type f es por que no queremos contar cosas que no son archivos.

-mount es para que no se escape de la partición y vuelva a contar a /home.

No recuerdo si herramientas forenses como Encase tienen alguna funcionalidad para detectar duplicados, porbablemente si pues una de sus funciones básicas es asociar un hash a cada archivo.

No me estoy metiendo con archivos de un sistema ajeno, son los míos, los conozco, esto es un complemento, no un sistema de gestión de archivos duplicados.

Si te preocupa el espacio, find te ayuda a encontrar los archivos grandes, sea cual sea tu criterio de grande:

find / -type f -size +1G -exec ls {} \; > 1g.txt

-size +1G significa mayores a un gigabyte

-exec significa ejecutá el siguiente comando hasta \; reemplazando {} con cada elemento hallado. Ojo que se va a ejecutar cada vez, si obtuviste mil archivos, van a haber mil ls, te aviso para cuando pongas un comando más pesadito.

Antes de cualquier limpieza está bueno medir:

df  / /home -h
Filesystem Size Used Avail Use% Mounted on
/dev/sda1 276G 41G 221G 16% /
/dev/sda6 612G 574G 7,4G 99% /home

-h significa mostrame los bytes con K, M, G

1% de disco libre puede parecer poco, pero tené en cuenta que hay varias carpetas por ahí que se llaman "*BORRABLE*", en realidad tengo %10 libre.

Pero había dicho que lo que importa es la salud, no es espacio, vamos a buscar duplicados.

Nombres



Asociar por nombres tiene problemas, hay archivos que los he bajado y les he cambiado el nombre, por lo general papers, que vienen con un código que no me sirve de nada, o las datasheets que bajan todas con el mismo nombre.


A veces me agarra la loca y al nombre de la datasheet le agrego una descripción.

Esta linea sirve para obtener todos los normbres de los archivos, contando las repeticiones:


find / -type f | rev | cut -d "/" -f 1 | rev  | sort | uniq -c | sort -n

rev lo necesito para el próximo paso, invierte el sentido del texto

cut corta en pedacitos separados por el delimitador...

-d "/" barra y me da el pedacito...

-f 1 primero. Esto es para quedarme con el nombre de archivo, lo que era lo último

rev necesito que esté al derecho otra vez

sort ordena alfabéticamente para juntar los iguales

uniq descarta duplicados mientras...

-c  cuenta cuantos duplicados hubo

sort nuevamente ordenamos según el número de repeticiones

-n numéricamente



Me pudiste haber preguntado por que no usar:

find / -type f -exec basename {} \; | sort | uniq -c | sort -nr

Recordá que antes te dije que -exec no es precisamente eficiente, el tiempo de ejecución paso de 11 segundos a... me aburrí de esperar, mientras hago otras cosa dejé escrito despues esto así me entero.

aplay /usr/share/sounds/speech-dispatcher/dummy-message.wav

aplay tira al sistema de audio el archivo de audio que le dés, ojo que no soporta cualquier cosa.


Hashes



Mejor buscar por hash, con md5 estamos bien, no estamos ante un adversario.

Recordemos que es un hash y que ocurre cuando hay un adversario de por medio.

Un hash es una función que se le aplica a un dato tal que "lo comprime" a un tamaño fijo, perdiendo un montón de información, pero con la característica de que distintos datos dan distintos hashes, hasta cierto punto.

La idea es que si dos archivos tienen la misma firma es problable que sean iguales.

Lo que tiene md5 es que el tamaño es un tanto reducido y que además no es seguro criptográficamente hablando, se pueden tomar atajos en las cuentas, no lo uses entonces si hay un adversario de por medio.

Puede haber usado sha1sum, sha2sum, etc, pero para lo que quiero, md5 alcanza y sobra.

find / -type f -iname "*.pdf" -o "*.epub" | while read BOOK; do
  md5sum "$BOOK"
done
-iname es para que busque cosas que terminen en pdf

-o es o lo siguiente

while toma cada linea y se la da a a read

read lee cada linea y la pone en la variable de nombre BOOK

md5sum calcula el hash md5 del archivo apuntado por $BOOK

Casi cuatro horas lleva esto...


Inspección


Como método complementario queda la inspección manual, tiene mucho de recuerdos, asociaciones, ver en que carpeta está cada cosa y qué lo rodea.


Ejemplo concreto


Primero un listado parcial de nombres de archivos con su frecuencia:

 3 Esapi-datasheet.pdf
2 msp430fr569xx_datasheet.pdf
2 BK-913-datasheet.pdf
1 spms376e_Tiva-TM4C123GH6PM_datasheet.pdf
1 sg90_datasheet.pdf
1 pic16f73-4-6-7-datasheet.pdf
1 msp430fr59x_69x_datasheet.pdf
1 e16g301_datasheet.pdf
1 datasheet.pdf
1 atmel-2586-avr-8-bit-microc....iny85_datasheet.pdf
1 atmel-2586-avr-8-bit-microc....iny85_Datasheet.pdf
1 AD9523-1_datasheet.pdf
1 utc uln2003 DARLINGTON SINK DRIVER.pdf
1 utc uln2003.pdf
Ahi hay dos pares de archivos probablemente el mismo con distintos nombres.

Estos son los hashes, todo recortado para que se vea bien:

c23db  /PlanDeEstu...uments/msp430fr569xx_datasheet.pdf
dbad8 /PlanDeEstu...sheets/AD9523/AD9523-1_datasheet.pdf
af564 /PlanDeEstu...2018/parallella/docs/e16g301_datasheet.pdf
b04c2 /INBOX/pasa...dspecs/atmel-2586-avr-8-bit-microco...iny85_datasheet.pdf
7f1c9 /INBOX/ANTE...ATABLE/robot/datasheet.pdf
f6e40 /INBOX/ANTE...ATABLE/robot/utc uln2003.pdf
c23db /REPO/githu...bricante/TI/msp430fr59x_69x_datasheet.pdf
bf45f /REPO/githu...AA_K60/Datasheets/BK-913-datasheet.pdf
bf45f /REPO/githu...EDU-NXP/Datasheets/BK-913-datasheet.pdf
c23db /SORT/free/...atasheet/TI/msp430fr569xx_datasheet.pdf
b04c2 /REPO/githu...h_cooler/doc/Atmel-2586-AVR-8-bit-Microco...iny85_Datasheet.pdf
a08eb /SORT/free/.../sg90_datasheet.pdf
f6e40 /SORT/free/.../components/utc uln2003 DARLINGTON SINK DRIVER.pdf
7a8ac /SORT/Secur...aining April 16th 2010/OWASP ESAPI/Esapi-datasheet.pdf
7a8ac /SORT/Secur...g/OWASP Default Training/OWASP ESAPI/Esapi-datasheet.pdf
7a8ac /SORT/Secur...aining May 28th 2010/OWASP ESAPI/Esapi-datasheet.pdf
fa658 /formacion/...sembly/spms376e_Tiva-TM4C123GH6PM_datasheet.pdf
aa14a /research/m...ntroller/pic/pic16f73-4-6-7-datasheet.pdf
Fijate que coincide la sumatoria del primer listado con la longitud de éste.

Ordenando por hashes, saltan los iguales:


7a8ac  /SORT/Secur...aining April 16th 2010/OWASP ESAPI/Esapi-datasheet.pdf
7a8ac  /SORT/Secur...aining May 28th 2010/OWASP ESAPI/Esapi-datasheet.pdf
7a8ac  /SORT/Secur...g/OWASP Default Training/OWASP ESAPI/Esapi-datasheet.pdf
7f1c9  /INBOX/ANTERIOR/RESCATABLE/robot/datasheet.pdf
a08eb  /SORT/free/.../sg90_datasheet.pdf
aa14a  /research/m...ntroller/pic/pic16f73-4-6-7-datasheet.pdf
af564  /PlanDeEstu...2018/parallella/docs/e16g301_datasheet.pdf
b04c2  /INBOX/pasa...dspecs/atmel-2586-avr-8-bit-microco...iny85_datasheet.pdf
b04c2 /REPO/githu...h_cooler/doc/Atmel-2586-AVR-8-bit-Microco...iny85_Datasheet.pdf
bf45f  /REPO/githu...AA_K60/Datasheets/BK-913-datasheet.pdf
bf45f  /REPO/githu...EDU-NXP/Datasheets/BK-913-datasheet.pdf
c23db  /PlanDeEstu...uments/msp430fr569xx_datasheet.pdf
c23db  /REPO/githu...bricante/TI/msp430fr59x_69x_datasheet.pdf
c23db  /SORT/free/...atasheet/TI/msp430fr569xx_datasheet.pdf
dbad8  /PlanDeEstu...sheets/AD9523/AD9523-1_datasheet.pdf
f6e40 /INBOX/ANTE...ATABLE/robot/utc uln2003.pdf
f6e40 /SORT/free/.../components/utc uln2003 DARLINGTON SINK DRIVER.pdf
fa658  /formacion/...sembly/spms376e_Tiva-TM4C123GH6PM_datasheet.pdf


<interrupción>

Se activó aplay ¡28 minutos y medio! termino el find con -exec, te recuerdo que del otro modo fueron 11 segundos.

</interrupción>


Finalmente, análisis manual:

7f1c9  /INBOX/ANTERIOR/RESCATABLE/robot/datasheet.pdf
es equivalente a

f6e40  /INBOX/ANTE...ATABLE/robot/utc uln2003.pdf
f6e40 /SORT/free/.../components/utc uln2003 DARLINGTON SINK DRIVER.pdf

¡Qué trabajito!, ¿no? Multiplicalo por mil.


Duplicados legítimos


Hay archivos que están duplicados y es legítimo, por ejemplo documentación del mismo componente en distintos repositorios. O esto:

35...c8  Vivado/2015.2/.../mig_7series_v1_9/.../redirect/ug586_7Series_MIS.pdf
35...c8  Vivado/2015.2/.../mig_7series_v1_9/.../ug586_7Series_MIS.pdf
35...c8  Vivado/2015.2/.../mig_7series_v1_8/.../redirect/ug586_7Series_MIS.pdf
35...c8  Vivado/2015.2/.../mig_7series_v1_8/.../ug586_7Series_MIS.pdf
35...c8  Vivado/2015.2/.../mig_7series_v1_7/.../redirect/ug586_7Series_MIS.pdf
35...c8  Vivado/2015.2/.../mig_7series_v1_7/.../ug586_7Series_MIS.pdf
35...c8  Vivado/2018.2/.../mig_7series_v1_9/.../redirect/ug586_7Series_MIS.pdf
35...c8  Vivado/2018.2/.../mig_7series_v1_9/.../ug586_7Series_MIS.pdf
35...c8  Vivado/2018.2/.../mig_7series_v1_8/.../redirect/ug586_7Series_MIS.pdf
35...c8  Vivado/2018.2/.../mig_7series_v1_8/.../ug586_7Series_MIS.pdf
35...c8  Vivado/2018.2/.../mig_7series_v1_7/.../redirect/ug586_7Series_MIS.pdf
35...c8  Vivado/2018.2/.../mig_7series_v1_7/.../ug586_7Series_MIS.pdf
35...c8  Vivado/2015.4/.../mig_7series_v1_9/.../redirect/ug586_7Series_MIS.pdf
35...c8  Vivado/2015.4/.../mig_7series_v1_9/.../ug586_7Series_MIS.pdf
35...c8  Vivado/2015.4/.../mig_7series_v1_8/.../redirect/ug586_7Series_MIS.pdf
35...c8  Vivado/2015.4/.../mig_7series_v1_8/.../ug586_7Series_MIS.pdf
35...c8  Vivado/2015.4/.../mig_7series_v1_7/.../redirect/ug586_7Series_MIS.pdf
35...c8  Vivado/2015.4/.../mig_7series_v1_7/.../ug586_7Series_MIS.pdf

18 archivos iguales, mala suerte, no se pueden borrar.

Me dirías que esos son parte de programas, no es asunto mío, no debería ni buscar por fuera de /home. Pués si lo es, por que hay programas y programas. Lo que están instalados por la distribución ok, es verdad que no importan, pero muchos otros como los de xilinx son independientes de la distribución, o pueden ser resultado de la compilación de un repositorio.

Como sea, nos encontramos con que hay "falsos positivos" como partes de programas, clones de versionamiento, carpetas tipo "BORRAR".

Lo que podría hacer es algo tipo

grep -v -f whitelist.txt

-v es que invierta la selección, esto es que me traiga lo que NO cumple

-f es una lista de rutas

Pero si me interesa cuando la duplicación se produce entre estos falsos positivos y lo que está fuera de esas rutas, puedo tener el archivo ug586_7Series_MIS.pdf
incluido en un curso, lo conservo. O puede estar en ~/Downloads, puedo borrarlo.



Ayuda al análisis manual

Hasta acá estas son mis reglas:

  • Hay paths que corresponden a aplicaciones, no me interesan los hallazgos a menos que coincidan con elementos fuera de esos paths.
  • Si están versionados, no me interesan los hallazgos a menos que coincidan con elementos fuera de esos paths.
  • Mejor ver de conjunto para detectar grupos de duplicación, producto de backups, snapshots, rescates y sincronizaciones.

Para ayudarme en este análisis, me he hecho un script en awk, un lenguaje extremadamente interesante, suele estar asociado a la herramienta sed que viene a ser algo así como automatizar las operaciones del programa vi, que es un editor de texto.

No te puedo contar el tiempo que me llevó escribir el siguiente script pues me dá vergüenza decir que fueron dos horas, es bastante básico pero aunque tengo experiencia con awk, tiene algunas sutilezas que voy a comentar. No soy experto pero lo vengo usándo hace décadas de modo esporádico, cuando hice este programa choqué con varias cosas y eso me indujo a escribir este post.

El programa lo que hace es:
  • Carga una lista de falsos positivos.
  • Recibe linea por linea algo del tipo "hash ruta" ordenado por hash.
  • Descarta ocurrencias simples de hash.
  • Señala los casos correspondientes a la lista de falsos positivos.

Comento el código señalando la resolución a las dificultades que tuve y lo que me llama la atención del lenguaje pensando que venís de C y bash.

awk soporta funciones pero sus returns no devuelven tipos complejos, no arrays, aunque se pueden usar globales mejor por referencia:

function load_whitelist(file, whitelist) {
   while (1) {
      status = getline record < file
      if (status == -1) {
         print "falla " file;
         exit 1;
      }
      if (status == 0) break;
      whitelist[++count] = record
   }
   close(file);
}

Atención, index devuelve la posición, no el desplazamiento...

function check_in_whitelist(path, whitelist) {
  for ( elem in whitelist ) {
    if ( index(path, whitelist[elem]) == 35 ) {
      return 1;
    }
  }
  return 0
}
function print_checked(line, whitelist) {
  if ( check_in_whitelist(line, whitelist) ) {
    print "XXX " line
  } else {
    print "    " line
  }
}

Recordemos que lo que esté en el bloque BEGIN se ejecuta una sola vez al comienzo del programa, viene a ser como el setup() en ArduinoIDE

BEGIN {
  state = "state_first"
  load_whitelist("whitelist.txt", whitelist)
}

He utilizado una FSM como lo relatado en ...

Las instrucciones estas se ejecutan para cada linea leida por STDIN, a la que se le aplica el pattern. Normalmente se hace mucho con esos patterns, pero en este caso que las lineas son todas homogéneas con esto alcanza.

Recordá que cada pattern y su bloque correspondiente se van evaluando y ejecutando en orden, con next podés pasar a procesar la siguiente linea de entrada sin aplicar las reglas restantes.

Recuerdo, estoy seguro, que en los patterns no se pueden usar variables.

/.*/ {
  switch (state) {
    case "state_first":
       linea_anterior = $0
       md5_anterior   = $1
       state = "state_head"
    break;
    case "state_head":
      if ($1 == md5_anterior) {
        print_checked(linea_anterior, whitelist)
        linea_anterior = $0
        state = "state_tail"
      } else {
       linea_anterior = $0
       md5_anterior   = $1
      }
    break;
    case "state_tail":
      if ($1 == md5_anterior) {
        print_checked(linea_anterior, whitelist)
        linea_anterior = $0
      } else {
        print_checked(linea_anterior, whitelist)
        print "======================================"
       linea_anterior = $0
        md5_anterior   = $1
        state = "state_head"
      }
    break;
  }
}

Finalmente, qué sorpresa, está el bloque END, para procesar la última línea:

END {
  if (state == "state_tail" ) {
    print_checked(linea_anterior, whitelist)
 }
}

Aplicándolo al ejemplo que vengo arrastrando:


    7a8ac  /SORT/Secur...aining April 16th 2010/OWASP ESAPI/Esapi-datasheet.pdf
7a8ac  /SORT/Secur...aining May 28th 2010/OWASP ESAPI/Esapi-datasheet.pdf
7a8ac  /SORT/Secur...g/OWASP Default Training/OWASP ESAPI/Esapi-datasheet.pdf
======================================
b04c2  /INBOX/pasa...dspecs/atmel-2586-avr-8-bit-microco...iny85_datasheet.pdf
b04c2 /REPO/githu...h_cooler/doc/Atmel-2586-AVR-8-bit-Microco...iny85_Datasheet.pdf
======================================
 bf45f  /REPO/githu...AA_K60/Datasheets/BK-913-datasheet.pdf
bf45f  /REPO/githu...EDU-NXP/Datasheets/BK-913-datasheet.pdf
======================================
c23db  /PlanDeEstu...uments/msp430fr569xx_datasheet.pdf
c23db  /REPO/githu...bricante/TI/msp430fr59x_69x_datasheet.pdf
c23db  /SORT/free/...atasheet/TI/msp430fr569xx_datasheet.pdf
======================================
f6e40 /INBOX/ANTE...ATABLE/robot/utc uln2003.pdf
f6e40 /SORT/free/.../components/utc uln2003 DARLINGTON SINK DRIVER.pdf

Y si en el archivo whitelist.txt ponemos alguna ruta, por ejemplo la de github:

    7a8ac  /SORT/Secur...aining April 16th 2010/OWASP ESAPI/Esapi-datasheet.pdf
7a8ac  /SORT/Secur...aining May 28th 2010/OWASP ESAPI/Esapi-datasheet.pdf
7a8ac  /SORT/Secur...g/OWASP Default Training/OWASP ESAPI/Esapi-datasheet.pdf
======================================
b04c2  /INBOX/pasa...dspecs/atmel-2586-avr-8-bit-microco...iny85_datasheet.pdf
XXX b04c2 /REPO/githu...h_cooler/doc/Atmel-2586-AVR-8-bit-Microco...iny85_Datasheet.pdf
======================================
XXX bf45f  /REPO/githu...AA_K60/Datasheets/BK-913-datasheet.pdf
XXX bf45f  /REPO/githu...EDU-NXP/Datasheets/BK-913-datasheet.pdf
======================================
c23db  /PlanDeEstu...uments/msp430fr569xx_datasheet.pdf
XXX c23db  /REPO/githu...bricante/TI/msp430fr59x_69x_datasheet.pdf
c23db  /SORT/free/...atasheet/TI/msp430fr569xx_datasheet.pdf
======================================
f6e40 /INBOX/ANTE...ATABLE/robot/utc uln2003.pdf
f6e40 /SORT/free/.../components/utc uln2003 DARLINGTON SINK DRIVER.pdf

con XXX marca cuales he decidido ignorar pero me sirven para saber que hacer, por ejemplo puedo borrar msp430fr569xx_datasheet.pdf si lo deseo, probablemente no, pues me gusta tener una carpeta con datasheets aunque queden duplicadas.


Por último, después de mirar muchos duplicados veremos que hay un patrón, puede ser muy útil aparte de identificar duplicación de archivos la duplicación aunque sea parcial de carpetas.

Cuando encontras varios archivos duplicados dentro de una misma carpeta, podés comparar las carpetas con el comando dirdiff, si no lo teneś:

sudo apt install dirdiff

Este programa te muestra canditatos a ser diferentes por fecha de modificación, pero no contenido, ni siquiera por longitud, no le confíes mucho, pero es indudablemente útil para ordenarte.

El código en github