Manufaturação industrial
Internet das coisas industrial | Materiais industriais | Manutenção e reparo de equipamentos | Programação industrial |
home  MfgRobots >> Manufaturação industrial >  >> Manufacturing Technology >> Processo de manufatura

Caixa realmente inteligente

Componentes e suprimentos

Folha de acrílico (por exemplo, perspex de 3 mm)
× 1
Arduino MKR FOX 1200
× 1
Amplificador de célula de carga SparkFun HX711
× 1
célula de carga de 5 kg
× 2
Adafruit BME280
× 1

Ferramentas e máquinas necessárias

Cortador a laser (genérico)
Impressora 3D (genérica)

Aplicativos e serviços online

Tinamous
Sigfox

Sobre este projeto


A plataforma Really Smart Box transforma uma Really Useful Box (tm) em uma caixa de armazenamento inteligente conectada à Internet para monitoramento de estoque. Com base no Sigfox Arduino MKR FOX 1200, ele detecta o peso das coisas armazenadas na caixa, junto com a temperatura e a umidade, e usa o rádio Sigfox de baixa potência para transmitir essas informações.





Caso de uso - armazenamento de filamentos de impressora 3D:


Se você possui uma impressora 3D, provavelmente se preocupará com a forma como o seu filamento é armazenado. Isso é verdade não apenas para o filamento da impressora, mas muitas outras coisas precisam ser armazenadas dentro de faixas aceitáveis ​​de temperatura e umidade (por exemplo, calafetagem de pintores pode se tornar inutilizável se exposto abaixo de temperaturas de congelamento).

Como uma das pessoas responsáveis ​​pela manutenção das impressoras 3D no meu espaço local do maker, preciso garantir que tenhamos estoque suficiente de filamento e que seja mantido seco.

Com a Caixa Realmente Inteligente consigo monitorar o peso do filamento e assim saber se estávamos baixando, junto com o monitoramento do nível de umidade na caixa para saber se a sílica gel precisa ser substituída.





Caso de uso - Controle de estoque de consumíveis:


Um empreiteiro de limpeza pode desejar manter um estoque de ambientador, sabonete ou outros consumíveis no local do cliente; o cliente provavelmente não permitiria o acesso WiFi do contratado ou para alimentar um dispositivo como este quando não estiver presente, no entanto, a empresa contratante precisa saiba quando enviar novo estoque, aumentando a sobrecarga do tempo da limpeza e uma papelada extra de que ninguém gosta.

A plataforma Really Smart Box simplesmente cai em uma caixa de armazenamento, porque usa Sigfox, não precisa se conectar à rede do cliente e tem baixo consumo de energia, então funciona com um conjunto de baterias. Como o conteúdo da caixa mudaria muito raramente, o Arduino pode ser mantido em um estado de baixa energia na maior parte do tempo, ajudando a estender a vida útil da bateria.

A plataforma pode ser informada sobre o peso do tipo de item armazenado na caixa (ou seja, os purificadores de ar) e, portanto, calcular quantos estão na caixa. Isso pode ser enviado para a empresa de limpeza contratada para alertá-los quando eles precisam de mais material para ser entregue no local do cliente.





Construindo a plataforma:


A plataforma é de construção simples, o corpo principal é feito de duas peças de acrílico cortado a laser (usei 3mm, mais grosso seria melhor para itens pesados ​​como filamento de impressora) com um par de células de carga entre elas.

Eu faço manualmente os buracos dos parafusos para a célula de carga para dar um acabamento melhor, eu ainda não encontrei um cortador a laser que faça o escareamento!

O acrílico pode ser cortado em qualquer tamanho para combinar com a caixa que você quiser, no entanto, preste atenção ao posicionamento das células de carga e ao comprimento do fio nelas, pois é bastante curto. A folha de acrílico superior é ligeiramente menor que a inferior para garantir que não prenda nas laterais da caixa.

A folha inferior tem um recorte para permitir que os componentes eletrônicos sejam montados no acrílico sem espaçadores e que as pernas soldadas do dispositivo sejam inseridas. Usei um macho M3 nas seções de canto do recorte para parafusar diretamente o PCB nele. Os pés impressos em 3D também foram colocados nos cantos para garantir que os parafusos da célula de carga que não estivessem nivelados com o acrílico não afetassem o equilíbrio.

O peso é medido usando duas células de carga de 5Kg. Um conjunto de 4 é normalmente usado em balanças de banheiro e isso poderia ser melhor, mas não consegui encontrar uma maneira legal de fixá-los no acrílico e também fornecer o espaçamento necessário para os eletrônicos.

As células de carga precisam de algum acolchoamento superior e inferior para permitir um pouco de flexão e os sensores reais do medidor de tensão (a parte branca na imagem) da célula de carga são mais grossos do que o bloco de montagem. Isso é obtido sob a célula de carga com as duas placas finais impressas em 3D "Really Smart Box" que têm um pequeno bloco para elevar a célula de carga, no topo da célula de carga estão alguns blocos de acolchoamento de acrílico cortados a laser.

As células de carga são conectadas a um amplificador de célula de carga HX711. Este possui dois canais (A e B) que podem ser selecionados, o que é perfeito para este uso.

Cada célula de carga é construída de medidores de tensão em uma configuração de ponte de Wheatstone, isso cria um par desequilibrado de divisores de potencial, quando a célula de carga é colocada sob carga, a resistência dos medidores de tensão muda e, portanto, uma diferença entre os dois divisores de potencial é criada , isso é amplificado e medido pelo HX711, que realiza a conversão de analógico para digital para nós.

Usei duas células de carga de 5 kg para este projeto, você pode obter classificações diferentes (por exemplo, 1 kg e 10 kg), que funcionam exatamente da mesma, mas com sensibilidade diferente.

Ao colocar as células de carga, certifique-se de que a seta na extremidade da célula está apontando para baixo (na direção da carga). As células têm orifícios roscados M5 em uma extremidade (geralmente a extremidade fixa) e M4 na outra (o lado em que você coloca a carga.

Os fios vermelho / preto são de alimentação, isso alimenta a parte superior e inferior dos divisores de potencial e é compartilhado entre as duas células de carga. Verde e branco são os fios de detecção do meio dos divisores de potencial, eles são conectados aos canais A e B no HX711.

O HX711 oferece suporte a 3 fatores de ganho, no entanto, eles também são usados ​​para seleção de canal. Ganhos de 128 e 64 estão disponíveis no canal A, onde selecionar um ganho de 32 seleciona o canal B. isso significa que nosso segundo canal não será tão sensível quanto o canal principal, isso é bom para esta aplicação.

O HX711 pode ser conectado a qualquer pino digital no Arduino, usei D0 (dados) e D1 (relógio), então o amplificador só precisa ser conectado à fonte 3v3 do Arduino.

Você pode ler mais sobre células de carga e o HX711 no excelente tutorial de células de carga do SparkFuns.

Finalmente, um BME280 é conectado ao barramento I2C e usado para detectar a temperatura e umidade dentro da caixa, isso também pode ser usado para detectar a pressão, no entanto, isso provavelmente é de pouco interesse e temos apenas 12 bytes de dados sigfox para brincar, então é não reportado.

A eletrônica é montada em uma placa ThingySticks Arduino Prototype, eu adicionei um suporte de bateria (hot melt colado na folha de acrílico inferior) e conectei a antena que tem um design plano e funcionou perfeitamente para a plataforma.





Calibração da célula de carga:


Antes de podermos usar a plataforma, as células de carga precisam ser calibradas. Cada célula de carga é única, é feita anexando um medidor de tensão a um bloco de metal e orifícios perfurados para fornecer flexão suficiente que pode ser detectada sem que se rompa. Por isso, precisamos calibrar cada célula de carga para sua resposta ao peso .

Depois de calibrado, aplicamos a equação y =mx + c ao valor ADC medido (x) para obter o peso real (y). Portanto, precisamos encontrar c (o deslocamento) em (a inclinação) para nossa célula de carga.

Tirei o topo da plataforma principal e coloquei um pequeno quadrado de acrílico em cada célula de carga por vez, e monitorei os valores medidos (é um roteamento no firmware para fazer isso que pode ser iniciado enviando um "c" para a porta serial.

Inicialmente a leitura de uma plataforma vazia foi medida, isso dá o valor do deslocamento (c), em seguida, colocando uma carga de peso conhecido na célula, a diferença nas leituras nos dá a inclinação.
  Inclinação =(medido - deslocamento) / peso (g).  

Usei uma pequena lata de ambientador (ca. 230g) e um carretel de filamento de impressora (ca. 1,5kg) para verificar os valores, ambos deram aproximadamente a mesma inclinação, o que foi reconfortante.

Naturalmente, o deslocamento medido com a pequena almofada de acrílico é diferente daquele experimentado com a folha superior inteira, da mesma forma, há também uma pequena diferença de inclinação ao usar ambas as células de carga, portanto, uma calibração secundária é necessária. Por enquanto, um deslocamento de zero ponto (tara) é usado, isso é definido no firmware, mas também pode ser definido usando a conexão serial USB ou por meio de uma mensagem de downlink Sigfox uma vez implantada.





Conexão Sigfox:


Com a caixa Really Smart conectada, usei inicialmente a porta serial USB para monitorar a saída e ajudar a depurar e ajustar o sistema. Desta forma, você pode ver as células de carga individuais, mudanças e ruído. No entanto, isso não vai funcionar para uma caixa implantada, pois precisa ser totalmente sem fio.

Com o Sigfox podemos enviar 12 bytes de dados até 140 vezes por dia para o nosso serviço online, isso é mais do que suficiente para o Really Smart Box. A estrutura de dados abaixo é usada no Arduino e descreve como usamos os 12 bytes.
  typedef struct __attribute__ ((empacotado)) sigfox_message {uint8_t status; // sinalizadores de status int8_t umidade; // umidade ::int:8 - alguns sensores (HTU21D) ler -ve umidade) int8_t temperatura; // temperatura ::int:8 (sem casas decimais). int16_t zeroWeight; // zeroWeight ::int:16:little-endian int16_t weight; // peso ::int:16:little-endian int16_t itemCount; // itemCount ::int:16:little-endian (100x contagem real de itens para permitir 2.01 (já que o peso não corresponderá exatamente) int8_t driftCorrection; // Correção de desvio para alterações no peso zero aplicado à balança. int8_t filler; // Nada para ver aqui, siga em frente .... int8_t lastStatus; // Último status sigfox} SigfoxMessage;  

O primeiro byte (status) é dividido em sinalizadores de bit para os problemas indicados:
  // status ::uint:8 -> Dividido em 8 bits // B7 - Primeira execução // B6 - Falha HX711 // B5 - Falha BME280 // B4 - Alarme de temperatura // B3 - Alarme de umidade // B2 - Alarme de peso // B1 - Estoque baixo // B0 - sobressalente  

Essa estrutura compacta até 12 bytes, no entanto, precisamos descompactá-la na extremidade Sigfox para empurrar para Tinamous. Usamos uma configuração de carga útil personalizada para isso e é melhor resolver isso conforme a estrutura de dados é definida. O nosso é:
  firstRun ::bool:7 hx711Fault ::bool:6 bmeFault ::bool:5 temperatureAlarm ::bool:4 umidadeAlarm ::bool:3 weightAlarm ::bool:2 lowStock ::bool:1 b0::bool:0 status ::int:8 umidade ::int:8 temperatura ::int:8 zeroWeight ::int:16:little-endian weight ::int:16:little-endian itemCount ::int:16:little -endian  

A carga útil personalizada divide nossos 12 bytes à medida que é analisada.

Observe que precisamos especificar a natureza little-endian de qualquer coisa maior que 1 byte, já que o padrão Sigfox é big-endian e o Arduino usa little-endian (ou seja, o byte menos significativo é o primeiro em palavras de vários bytes).

Observe também que dividir os sinalizadores booleanos no primeiro byte não progride o marcador de byte como acontece com todas as outras leituras, então o byte de status que contém todos os sinalizadores também é lido para pular o primeiro byte.

Incluídos nos sinalizadores estão os alarmes de temperatura, umidade e faixa de peso, poderíamos usar um serviço online (por exemplo, Tinamous) para monitorar temperatura, umidade e peso fora da faixa, no entanto, podem ter vida curta (algumas horas) e nossa caixa pode estar enviando com pouca frequência (uma ou duas vezes por dia), a possível condição ambiental prejudicial resultante pode ser facilmente perdida, portanto, eles são sinalizados no dispositivo e enviados (e redefinidos após um envio bem-sucedido).

A contagem de itens é, na verdade, definida como 100 vezes a contagem real de itens. Eu queria permitir valores como 2,2 itens (devido ao erro de peso ou outros itens na caixa) sem forçar um arredondamento, da mesma forma 2,95 pode ser arredondado para 2 se não formos cuidadosos e seria mais sugestivo de 3 itens em a caixa e um pequeno erro. Eu também não queria usar um float que exigiria mais espaço, então usei uma palavra de 16 bits e apliquei um fator para permitir uma conversão fácil (também é assinado para permitir um erro zero, o que pode resultar em um nível de estoque de -1 ou -2 etc.).

Muito pouco precisa ser feito para habilitar a comunicação Sigfox. Dentro do Arduino, a biblioteca Sigfox é adicionada e funções apropriadas são chamadas para enviar os dados de acordo com os exemplos do Arduino para a biblioteca Sigfox, no entanto, precisamos registrar nosso dispositivo com Sigfox.

O envio de um "s" para a porta serial do Really Smart Box imprime a ID Sigfox e o código PAC, que são usados ​​para ativar o dispositivo no backend Sigfox. Em seguida, vamos ao serviço de ativação de back-end Sigfox e seguimos o assistente, primeiro selecionando nosso dispositivo, em seguida, país / provedor e, em seguida, alguns detalhes.

E, finalmente, nosso dispositivo é ativado e listado:

Sigfox atribui dispositivos ao agrupamento de tipo de dispositivo que é sensato, pois você normalmente pode ter muitos (centenas, milhares, etc.) do mesmo tipo de dispositivo que você deseja atuar como um grupo. Com o tipo de dispositivo definido, podemos configurar um retorno de chamada personalizado para enviar os dados que recebemos ao nosso serviço online. Estou usando o Tinamous para isso (dica:veja o nome do meu perfil - posso ser tendencioso na minha seleção).





Usando a caixa realmente inteligente:


Uma vez conectado e aparafusado, o firmware é atualizado e as baterias instaladas na plataforma são simplesmente colocados em uma Caixa Realmente Útil (tm) e pronto para usar.

A energia deve ser aplicada o mais tarde possível, pois assim que o dispositivo for ligado, ele enviará a primeira mensagem Sigfox após 2 minutos e solicitará dados de downlink com ela. Esses dados podem incluir um comando "Zero" para zerar o peso das plataformas. Caso contrário, uma conexão serial USB é necessária ou aguardando a próxima solicitação de downlink - isso é feito a cada 12 horas.

Assim que estiver funcionando, a plataforma publicará mensagens Sigfox a cada 15 minutos para enviar o peso, contagem de itens, temperatura, umidade e estados de alarme. Temperatura, umidade e peso são medidos a cada minuto para garantir que não estejam fora do intervalo, com alarmes sendo sinalizados para a próxima transmissão, caso tenham sido acionados.





Monitoramento com Tinamous:


Tinamous oferece suporte a retornos de chamada personalizados Sigfox adicionando um "Sigfox Bot" à nossa conta, para obter instruções sobre como fazer isso, consulte meu tutorial "Obtenha seu Sigfox On" Hackster.io.

Ao adicionar um Sigfox Bot à sua conta Tinamous, se você incluir as configurações de API, o Sigfox Bot irá pesquisar seus dispositivos e adicioná-los à sua conta Tinamous, no entanto, você não precisa fazer isso porque o dispositivo será adicionado automaticamente quando os dados Está publicado.

Quando você adiciona o Bot, é apresentada uma tela de configuração de Callback para ajudar a configurar os Callbacks Sigfox.

Você pode então criar um retorno de chamada personalizado no Sigfox, observe que o Really Smart Box usa DATA -> retorno de chamada BIDIR que lida com o retorno de chamada UPLINK normal e os retornos de chamada BIDIR (up e downlink).

É aqui que a carga útil personalizada anterior se torna útil, cole-a do código-fonte na carga útil personalizada e atualize a seção de campos para refletir isso.

Lat e Lng são especificados neste retorno de chamada, o que dá uma localização aproximada, entretanto Sigfox no Arduino suporta configuração de localização aprimorada, mas isso precisa de um segundo retorno de chamada. Se você usar o recurso de localização geográfica, não especifique Lat / Lng nesta mensagem, pois a caixa realmente inteligente parecerá estar se movendo entre os locais.

Depois de configurado, ele também precisa ser habilitado para Downlink; isso é desabilitado por padrão, embora o BIDIR tenha sido definido.

Observe que a captura de tela abaixo da opção de Downlink está "marcada", isso deve ser feito manualmente e pode não estar disponível se o Tipo de Dispositivo não tiver sido definido como "CALLBACK" para dados de downlink (Tipo de Dispositivo -> Editar -> Dados de Downlink) .

Com um retorno de chamada de Downlink, também queremos especificar um retorno de chamada SERVIÇO -> RECONHECER para saber que nosso dispositivo recebeu os dados de downlink. Ao clicar no Sigfox Bot em Tinamous, outras configurações de callback são mostradas, siga as instruções para os callbacks de RECONHECIMENTO e GEOLOC.

Observe que você precisa copiar o cabeçalho de autorização do primeiro uplink / retorno de chamada bidir, pois esta é uma senha criptografada unilateralmente em Tinamous e não está mais disponível para exibição.

Com nossos retornos de chamada em vigor, os dados publicados por nosso dispositivo agora devem ser enviados para Tinamous. Também poderíamos adicionar chamadas de retorno de e-mail no Sigfox, o que pode ajudar a confirmar que os dados estão chegando (mas também pode ficar muito barulhento muito rapidamente).





Configurando o dispositivo Tinamous:


Uma vez que o dispositivo Sigfox foi visto no Tinamous (seja através da pesquisa da API ou um retorno de chamada), ele será mostrado na página Dispositivos, a partir daqui podemos editar as propriedades. Os campos são adicionados automaticamente conforme vêm do retorno de chamada Sigfox, portanto, é melhor esperar até que o dispositivo esteja publicando dados.

Eu defino o tempo "Not Reporting After" para 1 hora (atualmente publicando a cada 15 minutos) para que eu possa dizer se o dispositivo está quebrado e, opcionalmente, ser notificado sobre isso.

Eu não queria ver todos os campos enviados pelo dispositivo na página de gráfico / detalhes (são muitos se você incluir todas as sinalizações), então o Tinamous está configurado apenas para mostrar o peso e a contagem de itens. Rótulos e unidades amigáveis ​​ao ser humano também foram aplicados aqui.

O campo Contagem de itens é 100x a contagem real de itens, portanto, uma calibração é aplicada a esse campo para reduzi-la 100 vezes.

Alguns dados de downlink são configurados, o que fará com que o Really Smart Box zere e aplique limites de faixa de temperatura e umidade na próxima solicitação de mensagem de downlink (2 minutos após ligar e, a seguir, uma vez a cada 12 horas).





Visualizando as informações da caixa realmente inteligente:


Agora que os campos do dispositivo estão configurados, podemos monitorá-los por meio da página de detalhes do dispositivo (observe que não zerei a plataforma neste momento, portanto, ele pensa que 1/2 uma unidade está presente - também substituí a parte superior de acrílico por uma de 5 mm versão que é mais pesada, mas irá lidar melhor com o filamento da impressora).

Também podemos ver as interações de retorno de chamada Sigfox na seção Sigfox. Observe aqui que os dados do downlink estão sendo enviados e confirmados, no entanto, o Arduino está relatando um erro. Mais sobre isso no final.

Na guia Localização, também podemos ver onde está nossa Caixa Realmente Inteligente, o que pode ser útil se você esquecer em qual cliente ela está ou se estiver em uma van.

E, naturalmente, queremos uma boa visualização do painel de nossa Caixa Realmente Inteligente, a que está abaixo mostra o peso do conteúdo da caixa, as unidades estimadas nela e uma contagem de dispositivos que não estão reportando, para que possamos saber se algum está quebrado.





Recebendo notificações com Tinamous:


Em seguida, configurei o Tinamous para enviar um e-mail e sms quando a contagem de itens estiver baixa. Eu fiz isso especificando um intervalo de trabalho de 3 a 300 para o campo de contagem de itens. Se o valor estiver fora desta faixa, uma medição fora da faixa par é aumentada.

Adicionando uma notificação ao Tinamous, podemos ser notificados quando isso acontecer.

Poderíamos especificar apenas o campo no qual estamos interessados, mas deixar em branco nos dá notificações para qualquer campo que esteja fora do intervalo.

Da mesma forma para dispositivos, deixe em branco para todos os dispositivos (ou seja, o único que temos no momento)

Configure Repetir Notificações para disparar apenas uma vez, até que seja redefinido (todos os dias), caso contrário, notificações a cada 15 minutos se tornam irritantes muito rapidamente!

Em seguida, selecione como ser notificado, eu configuro para e-mail e sms e crio a notificação:





Conclusão:


Agora posso implantar o Really Smart Box e (com sorte) esquecê-lo. Quando o nível de estoque ficar baixo, serei notificado e posso verificar no painel para ver como está o desempenho. Usando o Sigfox, não preciso me preocupar com a alimentação do dispositivo, exceto por uma troca ocasional da bateria e nenhuma configuração de WiFi no local é necessária, tornando a implantação extremamente simples.

Estou planejando implantar esta unidade em uma de nossas caixas de armazenamento de filamentos em Cambridge Makespace para monitorar os níveis de estoque de filamentos.





Problemas a resolver:


Desnecessário dizer que este não é um projeto de qualidade de produção acabado, alguns problemas ainda precisam ser resolvidos:

Downlink Sigfox:

O Sigfox permite que 4 mensagens por dia sejam enviadas em resposta a uma mensagem de uplink. O Really Smart Box usa isso para permitir zerar novamente as balanças, definir faixas de temperatura e umidade superiores e inferiores e o peso do item. No entanto, ao tentar fazer isso funcionar, embora a mensagem de downlink pareça estar sendo enviada e sendo confirmada (como mostrado no back-end Sigfox), o Arduino está relatando um erro de status de 62, que não mapeia para qualquer sinalizador de erro condições listadas para o chip ATA8520, cavando os drivers, o comando usa uma solicitação de downlink que também não está listada na planilha de dados, portanto, mais investigações precisam ser feitas.

Somente depurar:

A execução das comunicações Sigfox com a depuração desabilitada faz com que a configuração de baixa potência do Arduino seja ativada e mata a porta serial USB.

Modo de baixo consumo:

Conforme descrito para a configuração de depuração Sigfox, o uso da biblioteca de baixa potência do Arduino faz com que o serial USB caia, portanto, não está habilitado neste momento.

Drift:

Nenhuma compensação para o desvio foi adicionada, sem dúvida as células de carga irão desviar quando mantidas sob carga constante.

Medições de ruído / não verticais:

É possível que a caixa Really Smart esteja na parte de trás de uma van (por exemplo, limpador de móveis, carpinteiro, encanador etc.). Seria bom adicionar um acelerômetro à plataforma e pular os ciclos de medição quando a caixa não estiver estável, da mesma forma, se a caixa não estiver na vertical, o peso não passará pelas células de carga como esperado.

Código

  • Código Arduino de caixa realmente inteligente
Código Arduino de caixa realmente inteligente Arduino
Adicionar bibliotecas para Arduino MKR FOX 1200, HX711, AdaFruit BME280, Arduino de baixa potência. Use o IDE do Arduino para programar normalmente.
 // Caixa Really Smart // Mede o peso do conteúdo de uma caixa realmente inteligente // Feita por duas folhas de acrílico com 2 células de carga entre elas // colocadas em uma caixa realmente smart box.// Também inclui um BME280 para medir a temperatura e pressão dentro da caixa.// Autor:Stephen Harrison // Licença:MIT # include  #include  #include  #include  #include  // ---------------------------------- ---- // BME280 na porta I2C.Adafruit_BME280 bme; // -------------------------------------- // amplificador de célula de carga HX711.// 0 :D0 - DOUT // 1:D1 - CLK // ganho inicial de 128.HX711 escalas (0, 1, 128); // Matrizes para células de carga. Índice 0 ==Canal A, Índice 1 ==Canal B.Ganho de flutuação [] ={128,32}; // Fatores de calibração.// usamos y =mx + c (c =deslocamento, m =fator de escala) ./ / para converter o valor medido em um peso.// Defina como o deslocamento relatado pelas células de carga.// sem peso sobre elas.float offset [] ={0,54940}; // Defina isso para o fator calculado quando um peso é colocado na balança.// Defina o deslocamento primeiro, acione novamente o arduiono para que isso tenha efeito // coloque um peso na balança e divida o valor bruto medido pelo weight.// using scaleFactor =valor medido / weight.float scaleFactor [] ={378.f, 260.9f}; // ---------------------- ---------------- // Sigfox // Esta é a estrutura de dados que publicamos no Sigfox.// Divida os bits como sinalizadores de bool do primeiro byte de status, mas o byte ainda precisa a ser // incluída, caso contrário, a umidade torna-se o status // firstRun ::bool:7 hx711Fault ::bool:6 bmeFault ::bool:5 temperatureAlarm ::bool:4 umidadeAlarm ::bool:3 weightAlarm ::bool:2 lowStock::bool:1 b0::bool:0// status::int:8 humidity::int:8 temperature::int:8 zeroWeight::int:16:little-endian weight::int:16:little-endian itemCount::int:16:little-endiantypedef struct __attribute__ ((packed)) sigfox_message { uint8_t status; // status::uint:8 -> Split to 8 bits // B7 - First run, B6 - HX711 fault, B5 BME280 fault, B4 Temperature alarm, B3 - Humidity alarm, B2 - weight alarm, B1 - Low stock, B0 - spare int8_t humidity; // humidity::int:8 (yes some sensors (HTU21D read -ve humidity) int8_t temperature; // temperature::int:8 (no decimal places). int16_t zeroWeight; // zeroWeight::int:16:little-endian int16_t weight; // weight::int:16:little-endian int16_t itemCount; // itemCount::int:16:little-endian (100x actual item count to allow for 2.01 (as weight won't match exactly) int8_t driftCorrection; // Drift Correction for changes in zero weight applied to the scales. int8_t filler; int8_t lastStatus; // Last sigfox status} SigfoxMessage;// Time the last Sigfox message was published atlong lastPublish =0;// Time the last Sigfox downlink was requested.// Allowed max 4 per day of these.long lastDownlink =0;uint8_t lastSigfoxStatus =0;// --------------------------------------// Application/state// If the fist cycle (after a reset) for the measure/publish// cycle (this is used to request a downlink message from Sigfox).// Note that only 4 of them are allowed per day so becareful// when deploying code.bool isFirstCycle =true;// Application mode// 0:Normal// 1:Calibrationint mode =0;// Which channel should be read during calibration.int calibrate_channel =0;// The last average value measured for each channel.float lastAverage[] ={0,0};// The current weight of the contents of the boxfloat currentWeight =0;// The weight of the units the box will hold.// Updatable via Sigfox downlink message.float unitWeight =238;// Different to tare as it would be a manual// zero'd at a set reading from scales// This will most likely change with drift (time/temperature/etc)// and should be set once the scale is in place but not loaded.// Updatable via Sigfox downlink message.float zeroWeight =0;bool bmeOk =true;bool hx711Ok =true;// Alarms and alarm rangesfloat minTemperature =5.f;float maxTemperature =60.f;float minHumidity =0.f;float maxHumidity =60.f;float maxWeight =10000; // 10kgbool temperatureAlarm =false;bool humidityAlarm =false;bool weightAlarm =false;float currentTemperature =0;float currentHumidity =0;float stockLevel =0;bool lowStock =false;float minStock =5;// Setup the Arduino.void setup() { pinMode(LED_BUILTIN, OUTPUT); //Initialize serial:Serial.begin(9600); // NB:The sensor I'm using (from random eBay seller) // does not use the default address. bmeOk =bme.begin(0x76); if (!bmeOk) { Serial.println("Could not find a valid BME280 sensor!"); } // Delay for USB Serial connect and for the BME's first reading. delay(5000); Serial.println("Really Smart Box..."); printHeader();}int delayCounter =0;void loop() { switch (mode) { case 0:measureAndPublish(); //Sleep for 1 minutes // Causing problems with USB connected. //LowPower.sleep(1 * 60 * 1000); delay(60 * 1000); pausa; case 1:calibrate(); atraso (1000); pausa; } // Check for user input via the serial port. checkSerial(); // measure is done on RTC timer tick (once per minute) delay(100);}void measureAndPublish() { // turn the LED on to indicate measuring. digitalWrite(LED_BUILTIN, HIGH); printBmeValues(); measureTemperatureAndHumidity(); measureWeight(true); // Weight, temperature and humidity are read every minute // however we only publish occasionally. if (shouldPublish()) { publishMeasurements(); } digitalWrite(LED_BUILTIN, LOW); }// Main measurement loop. Reads the weight from the load cells// and stores if no noise from the previous read.void measureWeight(bool printDetails) { scales.power_up(); atraso (500); float delta =readDelta(printDetails); if (printDetails) { Serial.print("\t"); Serial.print(delta, 2); } // If the delta is between -1 and 1 (i.e. no noise) // update the change in overall weight and units contained // otherwise ignore and try again later on. // This ensures we use only stable readings when both channels have not changed for // two sets of measurements. if (delta <1.f &&delta> -1.f) { // Remember the previous measured weight so we can get a delta. float lastWeight =currentWeight; // Compute the weight. Take the weight of both load cells // added together then subtract the zero'd weight. currentWeight =lastAverage[0] + lastAverage[1] - zeroWeight; // Compute the difference in weight of the items in the box // compated to the last time we had a stable reading. float itemsWeightDelta =currentWeight - lastWeight; updateStockLevels(); if (printDetails) { Serial.print("\t"); Serial.print("\t"); Serial.print(currentWeight, 2); Serial.print("\t"); // divide by unit weight to estimate the stock level in the box Serial.print(currentWeight / unitWeight, 2); Serial.print("\t"); // the change in weight, (i.e. the weight if the items added) Serial.print(itemsWeightDelta, 2); Serial.print("\t"); // divide by unit weight to estimate the units removed/added Serial.print(itemsWeightDelta / unitWeight, 2); } } checkWeightLimits(); if (printDetails) { Serial.println(); } // put the ADC in sleep mode and switch // off the LED now we're done measuring. scales.power_down(); }void updateStockLevels() { stockLevel =currentWeight / unitWeight; // Unlike other alarms the low stock level // is reset if the stock is re-stocked. lowStock =stockLevel  maxWeight ) { weightAlarm =true; } if (lastAverage[0]> (maxWeight /2)) { weightAlarm =true; } if (lastAverage[1]> (maxWeight /2)) { weightAlarm =true; }}// Read the difference in weight from the last // average to this time across both load cells.// average value is stored in the lastAverage array.float readDelta(bool printDetails) { float aDelta =readChannel(0, true); if (printDetails) { Serial.print("\t"); } float bDelta =readChannel(1, true); return aDelta + bDelta;}// Read the weight from a channel. Stores the measured value in // the lastAverage array and retuns the delta of the measured value// from the previous lastAverage. This allows us to know if the weight// has changed.// channel 0 =A// channel 1 =Bfloat readChannel(int channel, bool printDetails) { // Gain:// Channel A supports 128 or 64. Default 128 // Channel B supports 32 // Select Channel B by using gain of 32. scales.set_gain(gain[channel]); // HX711 library only has one set of offset/scale factors // which won't work for use as we use both channels and they // have different gains, so each needs to have it's offset/scale set // before reading the value. scales.set_offset(offset[channel]); scales.set_scale(scaleFactor[channel]); // force read to switch to gain. scales.read(); scales.read(); float singleRead =scales.get_units(); float average =scales.get_units(10); float delta =average - lastAverage[channel]; if (printDetails) { Serial.print(singleRead, 1); Serial.print("\t"); Serial.print(average, 1); Serial.print("\t"); Serial.print(delta, 1); Serial.print("\t"); } lastAverage[channel] =average; return delta;}// print the header for the debug data pushed out when measuring.void printHeader() { Serial.print("BME280\t\t\t\t\t"); Serial.print("Channel A\t\t\t"); Serial.print("Channel B\t\t\t"); Serial.print("\t\t"); Serial.print("Totals \t\t\t"); Serial.println(""); Serial.print("Temp\t"); Serial.print("Pressure\t"); Serial.print("Humidity\t"); Serial.print("read\t"); Serial.print("average\t"); Serial.print("delta\t"); Serial.print("\t"); Serial.print("read\t"); Serial.print("average\t"); Serial.print("delta\t"); Serial.print("\t"); Serial.print("sum\t"); Serial.print("\t"); Serial.print("weight\t"); Serial.print("items\t"); Serial.print("change\t"); Serial.print("items added"); Serial.println("");}// Calibration - reads/prints selected channel values.void calibrate() { digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level) scales.set_gain(gain[calibrate_channel]); scales.set_offset(offset[calibrate_channel]); scales.set_scale(scaleFactor[calibrate_channel]); // force read to switch to gain Serial.print("\t|CH:\t"); Serial.print(calibrate_channel,1); Serial.print("\traw:\t"); Serial.print(scales.read(),1); Serial.print("\t| raw:\t"); Serial.print(scales.read(),1); Serial.print("\t| units:\t"); Serial.print(scales.get_units(), 1); Serial.print("\t| gain:\t"); Serial.print(gain[calibrate_channel], 1); Serial.print("\t| factor:\t"); Serial.println(scaleFactor[calibrate_channel], 1); digitalWrite(LED_BUILTIN, LOW);}// check the serial port for input from a console to allow us to alter // the device mode etc.void checkSerial() { if(Serial.available()) { char instruction =Serial.read(); switch (instruction) { case '0':calibrate_channel =0; Serial.println("Channel 0 (A) Selected"); pausa; case '1':calibrate_channel =1; Serial.println("Channel 1 (B) Selected"); pausa; case 'm':// Measurement mode mode =0; Serial.println("Measurement Mode"); printHeader(); pausa; case 'c':// Calibration mode mode =1; Serial.println("Calibration Mode"); pausa; case 't':// Tare. Teset the scale to 0 Serial.println("Taring"); scales.power_up(); atraso (500); scales.tare(5); // Need to do this for each channel // and update our stored offset. Serial.println("Not properly Tared!"); pausa; case 'h':printHeader(); pausa; case 'z':zeroScales(); pausa; case 's':printSigfoxModelDetails(); pausa; default:Serial.println("Unknown instruction. Select:0, 1, m, c, t, h, z, or s"); Serial.println("m - measurement mode"); Serial.println("c - Calibration mode"); Serial.println("0 - Channel 0 (A) Calibration"); Serial.println("1 - Channel 1 (B) Calibration"); Serial.println("t - Tare (scale)"); Serial.println("z - Zero (Weight)"); Serial.println("h - print Header"); Serial.println("s - print Sigfox model details"); pausa; } }}// Measure (and record) the temperature and humidity levels// Sets alarms if out of rage (we can't use limits on Internet service side// as the messages may only be sent a few times a day and a brief (maybe hours)// out of range temperature/humidity could easily be missed between// message publishing.void measureTemperatureAndHumidity() { if (!bmeOk) { return; } currentTemperature =bme.readTemperature(); if (currentTemperature  maxTemperature) { temperatureAlarm =true; } currentHumidity =bme.readHumidity(); if (currentHumidity  maxHumidity) { humidityAlarm =true; }}// Print the values read from the BME280 sensorvoid printBmeValues() { //Serial.print("Temperature ="); Serial.print(bme.readTemperature(), 1); Serial.print("\t"); Serial.print(bme.readPressure() / 100.0F, 0); Serial.print("\t\t"); Serial.print(bme.readHumidity(),1); Serial.print("\t\t ");}// =============================================================// Sigfox handing// =============================================================// Determine if we should publish the Sigfox message.// We may also wish to publish if the stock level has// changed (or a significant weight level has changed)// but we would need to be careful of exceeding the // 140 messages per day for a noisy system.bool shouldPublish() { // Publish every 15 minutes // this doesn't really need to be this often // but whilst developing it helps keep an eye on the system. int messageIntervalMinutes =15; // On first run after reset // allow a 2 minute delay for the platform to be placed into // the box and stabalise before doing first publish // which is also expected to include a check for zeroing the platform. if (isFirstCycle) { messageIntervalMinutes =2; Serial.println("First cycle"); } // How long ago we last publish a Sigfox message long millisAgo =millis() - lastPublish; return millisAgo> (messageIntervalMinutes * 60 * 1000);}// Publish our measurements (weight, temperature, humidity etc)// to Sigfox.void publishMeasurements() { Serial.println("Sending via Sigfox..."); bool useDownlink =shouldUseDownlink(); if (useDownlink) { Serial.println("Using Sigfox downlink..."); } // stub for message which will be sent SigfoxMessage msg =buildMessage(); SigFox.begin(); SigFox.debug(); // Wait at least 30mS after first configuration (100mS before) delay(100); // Clears all pending interrupts SigFox.status(); delay(1); SigFox.beginPacket(); SigFox.write((uint8_t*)&msg, 12); // endPacket actually sends the data. uint8_t statusCode =SigFox.endPacket(useDownlink); printSigfoxStatus(statusCode); // Status =0 for a successful send, otherwise indicates // a failure. // Store when we last published a Sigfox message // to allow for timed message sending. if (statusCode ==0) { resetAlarms(); } // Update the last publish/downlink times // even if an error resonse was received to prevent // repeated publishing lastPublish =millis(); isFirstCycle =false; if (useDownlink) { parseDownlinkData(statusCode); lastDownlink =lastPublish; } // Store the status value lastSigfoxStatus =statusCode; SigFox.end();}void printSigfoxStatus(uint8_t statusCode) { Serial.print("Response status code :0x"); Serial.println(statusCode, HEX); if (statusCode !=0) { Serial.print("Sigfox Status:"); Serial1.println(SigFox.status(SIGFOX)); Serial1.println(); Serial.print("Atmel Status:"); Serial1.println(SigFox.status(ATMEL)); Serial1.println(); }}// Create the message to be publish to Sigfox.SigfoxMessage buildMessage() { SigfoxMessage message; message.status =getStatusFlags(); message.humidity =(int8_t )currentHumidity; message.temperature =(int8_t)currentTemperature; message.zeroWeight =(int16_t)zeroWeight; message.weight =(int16_t)currentWeight; message.itemCount =(int16_t)(stockLevel * 100); message.driftCorrection =0; // TODO message.filler =0; message.lastStatus =lastSigfoxStatus; return message;}// Get the status flags for the Sigfox message.byte getStatusFlags() { byte status =0; // B7 - First run, // B6 - HX711 fault // B5 - BME280 fault // B4 - Temperature alarm // B3 - Humidity alarm // B2 - weight alarm // B1 - Low stock // B0 - spare // Upper Nibble (Charging/Battery) // Battery flat if (isFirstCycle) { status |=0x80; // 1000 0000 } // HX711 fault. // we don't have a way to check this yet. if (!hx711Ok) { status |=0x40; // 0100 0000 } // BME280 fault if (!bmeOk) { status |=0x20; // 0010 0000 } // Over/Under temperature alarm if (temperatureAlarm> 0) { status |=0x10; // 0001 0000 } // Over/Under humidity alarm if (humidityAlarm) { status |=0x08; // 0000 1000 } // Over/under? weight alarm if (weightAlarm) { status |=0x04; // 0000 0100 } // if computed stock level low. if (lowStock) { status |=0x02; // 0000 0010 } return status;}// Determine if we are requesting a downlink message.bool shouldUseDownlink() { // When debugging uncomment this so as to not keep requesting // downlink //return false; // On first run we want to request a downlink // message to help with zero'ing and setup. if (isFirstCycle) { return true; } // How long ago we last did a downlink message. long millisAgo =millis() - lastDownlink; // try every 12 hours, this keeps us under the // maximum 4 per day. return millisAgo> (12 * 60 * 60 * 1000);}// Parse downlinked data.void parseDownlinkData(uint8_t statusMessage) { if (statusMessage> 0) { Serial.println("No transmission. Status:" + String(statusMessage)); Retorna; } // Max response size is 8 bytes // set-up a empty buffer to store this. (0x00 ==no action for us.) uint8_t response[] ={0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // Expect... // Byte 0:Flags // B7:Zero scales // B6:Set Temperature range (ignore min/max temp if 0) // B5:Set Humidity range (ignore min/max humidity if 0) // B4:Set tolerance? // B3:Set ??? // B2:Update unit weight (ignore update if 0) // B1:// B0:// Byte 1:Min T // Byte 2:Max T // Byte 3:Min Humidity // byte 4:Max Humidity // byte 5:Read tolerence??? (+/- x) // byte 6 &7:Unit weight // Parse the response packet from Sigfox if (SigFox.parsePacket()) { Serial.println("Response from server:"); // Move the response into local buffer. int i =0; while (SigFox.available()) { Serial.print("0x"); int readValue =SigFox.read(); Serial.println(readValue, HEX); response[i] =(uint8_t)readValue; i++; } // byte 0 - flags. // 0b 1000 0000 if (response[0] &0x80 ==0x80) { zeroScales(); } // 0b 0100 0000 if (response[0] &0x40 ==0x40) { updateTemperatureAlarm(response[1], response[2]); } // 0b 0010 0000 if (response[0] &0x20 ==0x20) { updateHumidityAlarm(response[3], response[4]); } // 0b 0000 0100 if (response[0] &0x04 ==0x04) { // Little Endian format. (ff dd -> 0xddff uint16_t weight =response[7] <<8 &response[6]; updateUnitWeight(weight); } } else { Serial.println("No response from server"); } Serial.println();}void printSigfoxModelDetails() { if (!SigFox.begin()) { Serial.println("Shield error or not present!"); return; } // Output the ID and PAC needed to register the // device at the Sigfox backend. String version =SigFox.SigVersion(); String ID =SigFox.ID(); String PAC =SigFox.PAC(); // Display module informations Serial.println("MKRFox1200 Sigfox configuration"); Serial.println("SigFox FW version " + version); Serial.println("ID =" + ID); Serial.println("PAC =" + PAC); Serial.println(""); Serial.print("Module temperature:"); Serial.println(SigFox.internalTemperature()); Serial.println("Register your board on https://backend.sigfox.com/activate with provided ID and PAC"); delay(100); // Send the module to the deepest sleep SigFox.end();}// =============================================================// General helper methods// =============================================================// Reset the alarms after they have been published.void resetAlarms() { temperatureAlarm =false; humidityAlarm =false; weightAlarm =false;}void zeroScales() { zeroWeight =lastAverage[0] + lastAverage[1]; Serial.print("Zero'd:"); Serial.print(zeroWeight, 1); Serial.println();}void updateTemperatureAlarm(int8_t lower, int8_t upper) { Serial.print("Setting temperature alarm. Min:"); Serial.print(lower); Serial.print(", Max:"); Serial.println(upper); minTemperature =lower; maxTemperature =upper;}void updateHumidityAlarm(int8_t lower, int8_t upper) { Serial.print("Setting humidity alarm. Min:"); Serial.print(lower); Serial.print(", Max:"); Serial.println(upper); minHumidity =lower; maxHumidity =upper;}void updateUnitWeight(uint16_t weight) { Serial.print("Setting unit weight:"); Serial.println(weight); unitWeight =weight;}
Really Smart Box Github Repository
https://github.com/Tinamous/ReallySmartBox

Peças personalizadas e gabinetes

Use this to cut the top and bottom acrylic sheets. cuttingguide_e7GNHf980M.svgThis sits between the lower acrylic sheet and load cell to raise it up a little and provide a edge to the platformsPrint 4 of these for each corner of the lower sheet if needed

Esquemas

Nothing to complex.

Processo de manufatura

  1. Caixa de suco
  2. Caixa preta
  3. Célula solar
  4. Bartender inteligente
  5. Semáforo inteligente
  6. Lixeira inteligente
  7. Robô da caixa de CD do Raspberry Pi
  8. Caixa UVC um esterilizador UV DIY
  9. Planta inteligente IoT
  10. Smart Blinds