Browse Source

Transférer les fichiers vers ''

Fichier pour la tête de dragon à Xano.
master
JeArz 6 months ago
parent
commit
b47fe06fc6
  1. 62
      avrTinyThreads.h
  2. 205
      firedrake.ino
  3. 103
      serialTrace.h
  4. 138
      therm100K.h

62
avrTinyThreads.h

@ -0,0 +1,62 @@ @@ -0,0 +1,62 @@
/*------------------------------------------------------------------*/
/* Projet: AVR tiny Threads */
/* File : avrTinyThreads.h */
/* Author: user@W500 greatly inpired by Adam Dunkels 08/28/20 */
/*------------------------------------------------------------------*/
#ifndef __avrTinyThreads_h__
#define __avrTinyThreads_h__
typedef struct AT {
unsigned short c;
} S_at ;
#define AT_ST_WAIT 0
#define AT_ST_QUIT 1
#define AT_ST_STOP 2
#define AT_SET(AT) (AT)->c = 0 ;
#define AT_BODY(params) char params // BODY
#define AT_START(AT) { \
char AT_YIELD_FLAG = 1; \
switch((AT)->c) { case 0: // START
#define AT_STOP(AT) } ; \
AT_YIELD_FLAG = 0; \
AT_SET(AT); \
return AT_ST_STOP; } // STOP
#define AT_QUIT(AT) \
do { \
AT_SET(AT); \
return AT_ST_QUIT; \
} while(0) // QUIT
#define AT_RESET(AT) \
do { \
AT_SET(AT); \
return AT_ST_WAIT; \
} while(0) // RESET
#define AT_WAIT_UNTIL(AT, exp) \
do { \
(AT)->c = __LINE__; \
case __LINE__: \
if(!(exp)) { \
return AT_ST_WAIT; \
} \
} while(0) // UNTIL
#define AT_WAIT_WHILE(AT, expr) \
AT_WAIT_UNTIL((AT), !(expr)) // WHILE
#define AT_RUN(fct) \
((fct) < AT_ST_QUIT) // RUN
#define AT_WAIT_THREAD(AT, thread) \
AT_WAIT_WHILE((AT), AT_RUN(thread)) // WAIT_THREAD
// checked for n calls in a thread : seems OK
#define AT_DELAY(AT, ms) \
do { \
static unsigned long _AT_ttl ; \
_AT_ttl = millis() + (unsigned int)(ms); \
AT_WAIT_UNTIL(AT, (millis() > _AT_ttl )); \
} while(0) // DELAY
// AT_WAIT_UNTIL(sat, millis() - started > cycleTime );
#define AT_SYN(AT, origin, ms) \
do { \ \
AT_WAIT_UNTIL(AT, (millis() - (origin) > (ms) )); \
} while(0) // DELAY
#endif // __avrTinyThreads_h__

205
firedrake.ino

@ -0,0 +1,205 @@ @@ -0,0 +1,205 @@
/*------------------------------------------------------------------*/
/* Projet: Firedrake 2020-09-03 */
/* File : firedrake.c */
/* Author: user@W500 */
/*------------------------------------------------------------------*/
// MODIFIED 10/28/21 user@khp: 2 pump & independant eyes
// Computed température in therm100K.h is 4° too hight
#include "avrTinyThreads.h" // pseudo threads for avr
//#define TEST // if uncommented then test
#define IHM // uncommented = log on serial monitor
#ifdef TEST
#define MAXHEAT 7 // WIP TEST in seconds => ~ 4 cycles ???
#define MAXTEMP 80.0 // WIP TEST in degree
#else
#define MAXHEAT 240 // seconds => 4mn => 120 cycles of 2s
#define MAXTEMP 150.0 // degree
#endif
#define MIMTEMP -270.0 // thermistor not present or broken
#define R_PUMP 13 // pump (BC337) yellow led, must move to pin 8
#define HEATER 12 // heater relay, blue test led
#define R_EYE 11 // ~ Eye red led
#define L_EYE 10 // ~ Eye red led
#define GAUGE 9 // ~ Gauge green led
#define KNOCK 8 // button (must move to another pin)
#include "serialTrace.h" // infos / trace to serial monitor
#include "therm100K.h" // Function for ntc thermistor 100K
// threads; Knock Eye Heat Pump Gauge Alert Vigil
static S_at atK, atE, atH, atP, atG, atA, atV ;
uint16_t th=2000, tp=3000, toff=2000 ;
// heatTime=0, eyes open, stay, close, wait knock
uint32_t started=0, cycleTime = ( 255 * 6 ) + 3000 + ( 255 * 12 ) + toff;
bool unSafe = false, tooHot =false, failedTherm=false, notEmpty=true, cycle=false;
void initIO() {
pinMode(KNOCK, INPUT_PULLUP);
pinMode(R_EYE, OUTPUT); pinMode(L_EYE, OUTPUT);
analogWrite(R_EYE, 0); analogWrite(L_EYE, 0); // Eyes unlighted
pinMode(HEATER, OUTPUT); pinMode(R_PUMP, OUTPUT);
digitalWrite(HEATER, LOW); digitalWrite(R_PUMP, LOW) ;
pinMode(GAUGE, OUTPUT); analogWrite(GAUGE, 254); // gauge at max
Th_Setup() ; // thermistor I/O setup
}
void setup() {
ttySetIHM(9600); ttyPutMem(); // IHM & show free mem
initIO(); // Setup: set LEDs & button
AT_SET(&atK); AT_SET(&atE); AT_SET(&atH); AT_SET(&atP);
AT_SET(&atA); AT_SET(&atG); AT_SET(&atV);
ttyPutUin("CycleTime = ", cycleTime);
int raw = Th_ReadRaw();
ttyPutInt("Raw temperature = ",raw);
float r_therm = Th_Compute_Therm_R(raw, TH_SERIE_R, PullUp);
ttyPutFloat("Thermistor bridge Ohm = ", r_therm);
float deg = Th_Compute_Simple_SH(r_therm); // uses Steinhart-Hart formula
ttyPutFloat("Th_Compute_Simple_SH = ", deg);
deg = Th_Get_Degre() ;
ttyPutFloat("Th_Get_Degre = ",deg);
}
/*------------------------------------------------------------------*/
void ledPWM(uint8_t led, uint8_t ratio) {
int val=constrain(ratio,0,254); // limit to values [0..254]
analogWrite(led, val);
}
bool getButton() { // haste hack to deflect bounces
int nb=0;
for (int i=0;i<10;i++) {
delay(10); // TODO ? : move in a thread ?
nb+= digitalRead(KNOCK); // Pullup pin: HIGH <=> button off
} // & LOW <=> button on
return (nb<5) ;
}
/*------------------------------------------------------------------*/
// Thread to brighten or darken eyes gradatory
// Use PWM from start to aim lasting a period, inc is >0 for up & <0 for down
static AT_BODY(atEye(S_at *sat, int start, int aim, int inc, int period))
{
static int i=0 ;
AT_START(sat);
ledPWM(R_EYE, start) ; ledPWM(L_EYE, start) ;
do {
AT_DELAY(sat, period);
i=i+inc;
ledPWM(R_EYE, i) ; ledPWM(L_EYE, i) ;
} while (i != aim );
AT_STOP(sat);
}
/*------------------------------------------------------------------*/
// Thread checking knocks
static AT_BODY(atKnock(S_at *sat))
{
AT_START(sat);
while (1) {
AT_WAIT_UNTIL(sat, getButton() );
cycle=true;
started=millis();
AT_WAIT_THREAD(sat, atEye(&atE,0,254,1,6)); // Opening
AT_DELAY(sat, tp);
AT_WAIT_THREAD(sat, atEye(&atE,254,0,-1,12)); // Closing
cycle=false;
AT_WAIT_UNTIL(sat, millis() - started > cycleTime ); // end time
}
AT_STOP(sat);
}
/*------------------------------------------------------------------*/
uint16_t addElapsed(uint16_t period) {
static uint32_t heatTime;
heatTime += period;
return (uint16_t) ( heatTime / 1000 ) ;
}
// Thread actuating heating
static AT_BODY(atHeater(S_at *sat, uint16_t offset, uint16_t period ))
{
static uint32_t former = 0; int burnt = 0;
AT_START(sat);
while( notEmpty && cycle ) { // cycle started if not empty
AT_DELAY(sat, period); // offset time
ttyPutStr("heat on");
former = millis();
digitalWrite(HEATER, HIGH);
AT_WAIT_UNTIL(sat, (millis() - former > period ) || unSafe ); // heating time
digitalWrite(HEATER, LOW);
burnt = addElapsed(period) ; ttyPutUin("Burning time: ", burnt);
notEmpty = (burnt <= MAXHEAT); // check if enough for next cycle
AT_WAIT_UNTIL(sat, millis() - started > cycleTime ); // end cycle time
}
AT_STOP(sat);
}
// Thread actuating pumping
static AT_BODY(atPump(S_at *sat, uint16_t offset, uint16_t period ))
{
AT_START(sat);
while( notEmpty && cycle ) {
AT_DELAY(sat, offset); //ttyPutStr("pump on");
digitalWrite(R_PUMP, HIGH);
AT_DELAY(sat, period);
digitalWrite(R_PUMP, LOW); // ttyPutStr("pump off");
AT_WAIT_UNTIL(sat, (millis() - started > cycleTime) || unSafe ); // end time
}
AT_STOP(sat);
}
/*------------------------------------------------------------------*/
// Thread to flash green led when action needed
static AT_BODY(atAlert(S_at *sat, int blink_temp, int blink_fail, int blink_empty))
{
AT_START(sat);
while ( ! notEmpty || unSafe ) {
if ( tooHot )
AT_DELAY(sat, blink_temp);
else if ( failedTherm )
AT_DELAY(sat, blink_fail);
else
AT_DELAY(sat, blink_empty);
digitalWrite(GAUGE, !digitalRead(GAUGE));
}
AT_STOP(sat);
}
// Thread liquid monitoring
static AT_BODY(atGauge(S_at *sat))
{
static uint16_t elapsed = 0, ratio = 0;
AT_START(sat);
while ( notEmpty && cycle ) {
ratio = map(addElapsed(0), 0, MAXHEAT, 254, 0);
ledPWM(GAUGE, ratio);
AT_WAIT_UNTIL(sat, millis() - started > cycleTime ); // end time
}
AT_STOP(sat);
}
// Thread temperature security
static AT_BODY(atVigil( S_at *sat, int watch ))
{
static float deg=0; static int ioMonitor = 0;
AT_START(sat);
deg = Th_Get_Degre(); // do the Steinhart-Hart formula for each call
while (deg < MAXTEMP && deg > MIMTEMP) {
AT_DELAY(sat, watch);
ioMonitor++;
if (ioMonitor > 40) { // wait 40 * watch ms to print Temperature
deg = Th_Get_Degre() ;
ttyPutFloat("Temperature = ",deg);
ioMonitor = 0;
// delay(1000); //TEST
}
}
ttyPutFloat("RISKY Temperature = ",deg);
// Prevent heating as temperature is out of safe range, & set alert
unSafe = true ; // impeach heater & pump
if (deg < MIMTEMP ) failedTherm = true ; // for gauge blinking
if (deg > MAXTEMP ) tooHot = true ; // for gauge blinking fast
AT_STOP(sat);
}
/*------------------------------------------------------------------*/
void loop() {
atVigil(&atV,200); // heat watch of 200ms
atKnock(&atK); // check for knock
atHeater(&atH,1500,th); // offset & duration for heater
atPump(&atP,1500,tp); // offset & duration for pump
atAlert(&atA,50,200,400); // set alerts to Gauge Led
atGauge(&atG); // monitor heat time
}
/*------------------------------------------------------------------*/

103
serialTrace.h

@ -0,0 +1,103 @@ @@ -0,0 +1,103 @@
/*------------------------------------------------------------------*/
/* Projet: Dev Tools */
/* File : serialTrace.h */
/* Author: user@W500 08/28/20 */
/*------------------------------------------------------------------*/
// RCS CI/CO : Cx v v, terminate log : CcCc, view log : Cx v l
// TODO: all this should be in a class or a singleton class
// TODO: strings should be in Flash 32kb code
// #include <avr/pgmspace.h>
#ifndef __SERIALTRACE_H__
#define __SERIALTRACE_H__
extern char *__brkval;
unsigned long freeMemory() {
char top;
return &top - (__brkval ? __brkval : __malloc_heap_start);
}
void retard(unsigned long ms, int mini) {
#ifndef TEST
delay(ms);
#else
delay(mini);
#endif
}
// functions used for I/O using serial monitor
#ifndef IHM // if IHM undefined then just empty functions.
// So users don't need to change code, just define/undefine IHM.
// A bit of a waste, but main purpose is testing.
bool ttyAskN ( String prompt ) {}
bool ttyAskY ( String prompt ) {}
char ttyGetChar ( String prompt ) {}
int ttyGetInt ( String prompt ) {}
void ttyPutInt ( String prompt, int num ) {}
void ttyPutMem ( ) {}
void ttyPutStr ( String txt ) {}
void ttyPutUin ( String prompt, unsigned int num ) {}
void ttyPutFloat ( String prompt, float num ) {}
void ttySetIHM ( long baud ) {}
void ttyWipe ( short lines ) {}
#else
void ttySetIHM(long baud) {
Serial.begin(baud); // WIP check if baud in correct values ?
for (int i=0;i<10;i++) Serial.print("\n\n\n") ;
}
void ttyPutStr(String txt) { Serial.println(txt);
}
void ttyPutMem() {
char buf[32];
unsigned long octets = freeMemory();
int k = octets / 1024 ; int o = octets % 1024;
sprintf(buf, "SRAM: %dKo%d free",k,o);
Serial.println (buf) ;
}
void ttyPutInt(String prompt, int num ) {
char buf[64];
sprintf(buf, "%s %d", prompt.c_str(), num ) ;
Serial.println (buf) ;
}
void ttyPutUin(String prompt, unsigned int num ) {
char buf[64];
sprintf(buf, "%s %d", prompt.c_str(), num ) ;
Serial.println(buf) ;
}
int ttyGetInt(String prompt) {
Serial.println(prompt);
while (Serial.available()==0) {} //Wait for user input
String val=Serial.readString();
return val.toInt();
}
void ttyPutFloat ( String prompt, float num ) {
Serial.print(prompt);
Serial.println (num) ;
}
void ttyPutFloatRound ( String prompt, float num ) {
char buf[64];
sprintf(buf, "%s %d", prompt.c_str(), int(num) ) ;
Serial.println (buf) ;
}
char ttyGetChar(String prompt) {
Serial.println(prompt);
while (Serial.available()==0) {} //Wait for user input
return Serial.read();
}
bool ttyAskY(String prompt){
byte R = 223 & (byte)ttyGetChar(prompt);
if ('Y' == R ) return true;
else return false ;
}
bool ttyAskN(String prompt){
byte R = 223 & (byte)ttyGetChar(prompt);
if ('N' == R ) return true;
return false ;
}
void ttyWipe (short lines) {
//const short ESC=27 ; KO in Arduino serial monitor, works with others tty
// Serial.write(ESC); Serial.print("[2J"); // cls
// Serial.write(ESC); Serial.print("[H"); // cursor to home
while (lines--) Serial.print("\n") ; // somehow overkill !
}
#endif // IHM
#endif // __SERIALTRACE_H__

138
therm100K.h

@ -0,0 +1,138 @@ @@ -0,0 +1,138 @@
/*----------------------------------------------------------------------------*/
/* Projet: Firedrake */
/* File : therm100K.h */
/* Author: user@W500 09/20/20 */
/*----------------------------------------------------------------------------*/
/*
BRIDGE PULL UP MOUNTING (Pulldown is opposite)
TH_VCC Analog In: TH_PIN TH_GND
| | |
x----/\/\/\/\/\/\/\/\/\/\/\/\/\-----x----/\/\/\/\/\/\/\/\/\/\/\/\/\--------x
Serie Resistor: TH_SERIE_R Thermistor: TH_R0, TH_T0, TH_BETA
*/
/*----------------------------------------------------------------------------*/
// Tested on Uno board.
// Using direct computing of simplyfied Seinhart-Hart takes 330µs.
// Using 30 values look up table is 3 time less time, 100µs & error less than 1°.
// For the time being we do the math for each call, choice should be fixed
// at setup and Th_Get_Degre() should take it in account.
// TODO: all this should be in a class in a library
#ifndef __THERM100K_H__
#define __THERM100K_H__
#define TH_VCC 2 // digital output for bridge supply
#define TH_GND 6 // digital output for bridge ground
#define TH_PIN 5 // adc pin to bridge middle pont
#define TH_ADCSTEPS 1023 // analog max value 2^(adc digits)-1
// WIP: we use standard values for this kind of thermistor,
// but this yield a température 4° too high
// #define TH_R0 119.4 // nominal resistance in KOhm at T0°C => 4° too much at 18°
#define TH_R0 103.9 // nominal resistance in KOhm at T0°C => -.2°/+.6° at 18,5°
#define TH_T0 27.2 // temp at nominal resistance (mostly 25°C)
#define TH_BETA 3.58 // thermistor beta coeff [3-4] Kelvin
#define TH_SERIE_R 4.7 // Serie resistor
#define TH_MOUNT_R PullUp // Serie mounting in "PullUp" or "PullDown"
#define TH_KILO 1000.0
enum T_SeriePull { PullUp, PullDown }; // discriminate serie resistor mounting
float Th_Compute_Therm_R(int raw, float rSerie, T_SeriePull rPull);
float Th_Compute_Simple_SH(float r_therm);
#define LUT_NB 30 // Look up table count, 30 values takes 10ms to build
struct {
int adc; float deg;
} Th_Lut[LUT_NB]; // Look up table
void Th_PrLookUp() {
#ifdef IHM
for (int i=0; i < LUT_NB; i++) { Serial.print("LUT adc "); Serial.print(Th_Lut[i].adc);
Serial.print(" => "); Serial.print(Th_Lut[i].deg); Serial.println("°C");}
#endif
}
/* This takes 9880 µseconds for 30 values on an UNO board. */
void Th_MkLookUp(){ // builds a look up table adc <=> deg
float r_therm = 0; int adcVal = 1, astep = floor(float(TH_ADCSTEPS) /(LUT_NB-1));
for (int i=0; i < LUT_NB; i++) {
Th_Lut[i].adc = adcVal;
r_therm = Th_Compute_Therm_R(adcVal, TH_SERIE_R, TH_MOUNT_R);
Th_Lut[i].deg = Th_Compute_Simple_SH(r_therm) ;
adcVal += astep ;
}
}
float Th_LookUpFind(int raw){
float result=0, t ; int last = LUT_NB -1, idx_out=-1; // index for out array
if (raw <= Th_Lut[0].adc) idx_out = 0;
else if (raw >= Th_Lut[last].adc) idx_out = last;
if (idx_out >= 0) {
result = float(Th_Lut[idx_out].deg) ; // out of bounds: first or last
#ifdef IHM
Serial.print("LUT: out of bounds at = "); Serial.println(idx_out);
#endif
}
else {
int pos = 1; // [0] allready tested
while(raw > Th_Lut[pos].adc) pos++; // looking for upper bound
if (raw == Th_Lut[pos].adc) result = float(Th_Lut[pos].deg); // exact match
else { // interpolate between pos-1 & pos
float part = 0;
part = float(raw - Th_Lut[pos-1].adc);
part /= float(Th_Lut[pos].adc - Th_Lut[pos-1].adc);
result = Th_Lut[pos-1].deg + part * (Th_Lut[pos].deg - Th_Lut[pos-1].deg);
}
}
return result;
}
void Th_Setup() {
pinMode(TH_VCC, OUTPUT);
pinMode(TH_GND, OUTPUT);
digitalWrite(TH_VCC, LOW);
digitalWrite(TH_GND, LOW);
analogReference(DEFAULT);
}
int Th_ReadRaw() {
int raw = 0;
digitalWrite(TH_VCC, HIGH);
delay(500); // WIP: give some time to reach 5V ?
raw = analogRead(TH_PIN);
digitalWrite(TH_VCC, LOW);
return raw;
}
/* This takes 80 µseconds on an UNO board. */
float Th_Compute_Therm_R(int raw, float rSerie, T_SeriePull rPull) {
float val = 0 ;
switch (rPull) {
case(PullUp): val = rSerie * TH_KILO / ((float(TH_ADCSTEPS) / raw) - 1);
break;
case(PullDown): val = rSerie * TH_KILO * ((float(TH_ADCSTEPS) / raw) - 1);
break;
#ifdef IHM
default: Serial.println(); Serial.println( "BUG in Th_Compute_Therm_R" );
Serial.println();
#endif
}
return val;
}
/* This is computed using the simplified Steinhart-Hart formula
and takes 250 to 270 µseconds on an UNO board.
If more speed is needed, you may load a look up table and
interpolate the ADC reading.
*/
float Th_Compute_Simple_SH(float r_therm) {
float s_h = 0;
s_h = r_therm / (TH_R0 * TH_KILO); // R/R0
s_h = log(s_h); // ln(R/R0 )
s_h /= TH_BETA * 1000.0; // 1/B * ln(R/R0 )
s_h += 1.0 / (25.0 + 273.15 ); // + 1/T0 in Kelvin
s_h = 1.0 / s_h ; // invert
s_h = s_h - 273.15; // in Celsius
return s_h;
}
float Th_Get_Degre () {
int raw = Th_ReadRaw();
float r_therm = Th_Compute_Therm_R(raw, TH_SERIE_R, TH_MOUNT_R);
return Th_Compute_Simple_SH(r_therm);
}
#endif // __THERM100K_H__
Loading…
Cancel
Save